kraken_bridge 0.1.0 → 0.2.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 +6 -0
- data/IMPLEMENTATION_SUMMARY.md +284 -0
- data/README.md +7 -2
- data/examples/test.rb +129 -0
- data/lib/kraken_bridge/client.rb +82 -0
- data/lib/kraken_bridge/configuration.rb +38 -0
- data/lib/kraken_bridge/errors/api_error.rb +19 -0
- data/lib/kraken_bridge/errors/authentication_error.rb +11 -0
- data/lib/kraken_bridge/errors/base_error.rb +14 -0
- data/lib/kraken_bridge/errors/connection_error.rb +11 -0
- data/lib/kraken_bridge/errors/rate_limit_error.rb +14 -0
- data/lib/kraken_bridge/errors/validation_error.rb +15 -0
- data/lib/kraken_bridge/middleware/error_handler.rb +166 -0
- data/lib/kraken_bridge/middleware/logger.rb +126 -0
- data/lib/kraken_bridge/middleware/rate_limiter.rb +29 -0
- data/lib/kraken_bridge/middleware/retry_handler.rb +97 -0
- data/lib/kraken_bridge/rest/authentication.rb +20 -0
- data/lib/kraken_bridge/rest/client.rb +20 -0
- data/lib/kraken_bridge/rest/connection.rb +86 -0
- data/lib/kraken_bridge/rest/endpoints/account.rb +92 -0
- data/lib/kraken_bridge/rest/endpoints/base.rb +23 -0
- data/lib/kraken_bridge/rest/endpoints/market_data.rb +58 -0
- data/lib/kraken_bridge/rest/endpoints/trading.rb +96 -0
- data/lib/kraken_bridge/rest/request.rb +22 -0
- data/lib/kraken_bridge/rest/response.rb +35 -0
- data/lib/kraken_bridge/utils/nonce_generator.rb +11 -0
- data/lib/kraken_bridge/utils/signature.rb +18 -0
- data/lib/kraken_bridge/version.rb +1 -1
- data/lib/kraken_bridge/websocket/client.rb +131 -0
- data/lib/kraken_bridge/websocket/connection.rb +88 -0
- data/lib/kraken_bridge/websocket/message_handler.rb +39 -0
- metadata +31 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 103cc2bb62587418aaaf66eaa998e57c469595696460f9a3065ed2d6d5c4a5ed
|
|
4
|
+
data.tar.gz: 76c17abf78c12ba5c7abaaa7de77e795df2f8cf47a8b2564571ee4f8c409bfef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: aa6ec0109f7c6de1850e3281d9c03942638cb70591097e2a4202fa597bffeb3579713fb34282fb0beefd0bf7f124631a722d421b1a0200a533ed13e198fbd782
|
|
7
|
+
data.tar.gz: cbb0985a92677ed4f7cfd4524af587f575b24599aa11aa8f95f93f3a62322cb907dd4ed68bd04d1027284bd238fea545bf443c65676be233f9be6b60a4bece20
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [0.2.0] - 2026-03-28
|
|
4
|
+
|
|
5
|
+
- Added Spot REST trading wrappers for `AmendOrder`, `AddOrderBatch`, `CancelOrderBatch`, and `GetWebSocketsToken`
|
|
6
|
+
- Expanded `CancelOrder` to support `txid`, `userref`, and `cl_ord_id`
|
|
7
|
+
- Added request specs for private trading and account endpoint parameter mapping
|
|
8
|
+
|
|
3
9
|
## [0.1.0] - 2025-12-02
|
|
4
10
|
|
|
5
11
|
- Initial release
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
# KrakenBridge Gem - Complete Implementation Summary
|
|
2
|
+
|
|
3
|
+
## ✅ Implementation Complete - Ready for Publishing
|
|
4
|
+
|
|
5
|
+
### What's Included
|
|
6
|
+
|
|
7
|
+
#### 1. **REST API - Complete Implementation**
|
|
8
|
+
|
|
9
|
+
**Public Endpoints (Market Data):**
|
|
10
|
+
- `time` - Server time
|
|
11
|
+
- `system_status` - System status
|
|
12
|
+
- `assets` - Asset information
|
|
13
|
+
- `asset_pairs` - Asset pair information
|
|
14
|
+
- `ticker` - Ticker data
|
|
15
|
+
- `ohlc` - OHLC candlestick data
|
|
16
|
+
- `order_book` - Order book (depth)
|
|
17
|
+
- `trades` - Recent trades
|
|
18
|
+
- `spread` - Bid/ask spread
|
|
19
|
+
|
|
20
|
+
**Private Endpoints (Account):**
|
|
21
|
+
- `balance` - Account balance
|
|
22
|
+
- `extended_balance` - Extended balance
|
|
23
|
+
- `trade_balance` - Trade balance
|
|
24
|
+
- `open_orders` - Open orders
|
|
25
|
+
- `closed_orders` - Closed orders
|
|
26
|
+
- `query_orders` - Query specific orders
|
|
27
|
+
- `trades_history` - Trade history
|
|
28
|
+
- `query_trades` - Query specific trades
|
|
29
|
+
- `open_positions` - Open positions
|
|
30
|
+
- `ledger` - Account ledger
|
|
31
|
+
- `query_ledger` - Query ledger entries
|
|
32
|
+
- `trade_volume` - Trade volume
|
|
33
|
+
- `request_export_report` - Request export
|
|
34
|
+
- `get_export_report` - Get export
|
|
35
|
+
|
|
36
|
+
**Private Endpoints (Trading):**
|
|
37
|
+
- `add_order` - Place a new order
|
|
38
|
+
- `edit_order` - Edit existing order
|
|
39
|
+
- `cancel_order` - Cancel single order
|
|
40
|
+
- `cancel_all_orders` - Cancel all orders
|
|
41
|
+
- `cancel_all_orders_after` - Cancel after timeout
|
|
42
|
+
|
|
43
|
+
#### 2. **WebSocket - Public Feeds Implementation**
|
|
44
|
+
|
|
45
|
+
**Supported Channels:**
|
|
46
|
+
- **Ticker** - Real-time ticker updates
|
|
47
|
+
- **Trades** - Trade feed
|
|
48
|
+
- **OHLC/Klines** - Candlestick data
|
|
49
|
+
- **Spread** - Bid/ask spread updates
|
|
50
|
+
|
|
51
|
+
**Features:**
|
|
52
|
+
- Automatic connection management
|
|
53
|
+
- Subscribe/unsubscribe functionality
|
|
54
|
+
- Callback-based message handling
|
|
55
|
+
- Error handling and logging
|
|
56
|
+
- Connection status tracking
|
|
57
|
+
|
|
58
|
+
#### 3. **Authentication & Security**
|
|
59
|
+
|
|
60
|
+
- ✅ HMAC-SHA512 signature generation (Kraken spec compliant)
|
|
61
|
+
- ✅ Millisecond-precision nonce generation
|
|
62
|
+
- ✅ Automatic nonce injection into all authenticated requests
|
|
63
|
+
- ✅ 2FA (OTP) support on all authenticated endpoints
|
|
64
|
+
- ✅ Base64 secret decoding
|
|
65
|
+
- ✅ Request body ordering for signature validation
|
|
66
|
+
|
|
67
|
+
#### 4. **Core Features**
|
|
68
|
+
|
|
69
|
+
- ✅ Rate limiting (configurable)
|
|
70
|
+
- ✅ Automatic retries with exponential backoff
|
|
71
|
+
- ✅ Comprehensive error handling
|
|
72
|
+
- ✅ Request/response logging
|
|
73
|
+
- ✅ Configuration management
|
|
74
|
+
- ✅ Standardized response objects
|
|
75
|
+
- ✅ Custom error classes
|
|
76
|
+
|
|
77
|
+
#### 5. **Documentation**
|
|
78
|
+
|
|
79
|
+
**Files Created/Updated:**
|
|
80
|
+
- `README.md` - Comprehensive guide with:
|
|
81
|
+
- Quick start examples
|
|
82
|
+
- Configuration options
|
|
83
|
+
- Full API reference
|
|
84
|
+
- Error handling patterns
|
|
85
|
+
- Security recommendations
|
|
86
|
+
- Troubleshooting guide
|
|
87
|
+
- References and links
|
|
88
|
+
|
|
89
|
+
- `AUTHENTICATION_FIXES.md` - Technical details on authentication implementation
|
|
90
|
+
- `TESTING_AUTHENTICATED_ENDPOINTS.md` - Testing guide with examples
|
|
91
|
+
|
|
92
|
+
### File Structure
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
kraken_bridge/
|
|
96
|
+
├── lib/
|
|
97
|
+
│ ├── kraken_bridge.rb (main entry point)
|
|
98
|
+
│ └── kraken_bridge/
|
|
99
|
+
│ ├── client.rb (main client)
|
|
100
|
+
│ ├── configuration.rb
|
|
101
|
+
│ ├── version.rb
|
|
102
|
+
│ ├── rest/
|
|
103
|
+
│ │ ├── client.rb
|
|
104
|
+
│ │ ├── connection.rb
|
|
105
|
+
│ │ ├── request.rb
|
|
106
|
+
│ │ ├── response.rb
|
|
107
|
+
│ │ ├── authentication.rb
|
|
108
|
+
│ │ └── endpoints/
|
|
109
|
+
│ │ ├── base.rb
|
|
110
|
+
│ │ ├── market_data.rb
|
|
111
|
+
│ │ ├── account.rb
|
|
112
|
+
│ │ └── trading.rb
|
|
113
|
+
│ ├── websocket/
|
|
114
|
+
│ │ ├── client.rb
|
|
115
|
+
│ │ ├── connection.rb
|
|
116
|
+
│ │ └── message_handler.rb
|
|
117
|
+
│ ├── utils/
|
|
118
|
+
│ │ ├── nonce_generator.rb
|
|
119
|
+
│ │ └── signature.rb
|
|
120
|
+
│ ├── middleware/
|
|
121
|
+
│ │ ├── rate_limiter.rb
|
|
122
|
+
│ │ ├── retry_handler.rb
|
|
123
|
+
│ │ ├── logger.rb
|
|
124
|
+
│ │ └── error_handler.rb
|
|
125
|
+
│ └── errors/
|
|
126
|
+
│ ├── base_error.rb
|
|
127
|
+
│ ├── api_error.rb
|
|
128
|
+
│ ├── authentication_error.rb
|
|
129
|
+
│ ├── rate_limit_error.rb
|
|
130
|
+
│ ├── connection_error.rb
|
|
131
|
+
│ └── validation_error.rb
|
|
132
|
+
├── spec/ (test files)
|
|
133
|
+
├── README.md
|
|
134
|
+
├── CHANGELOG.md
|
|
135
|
+
├── LICENSE.txt
|
|
136
|
+
├── Gemfile
|
|
137
|
+
├── kraken_bridge.gemspec
|
|
138
|
+
└── Rakefile
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Usage Examples
|
|
142
|
+
|
|
143
|
+
#### Public API
|
|
144
|
+
```ruby
|
|
145
|
+
client = KrakenBridge::Client.new
|
|
146
|
+
ticker = client.market_data.ticker(['XBTUSD'])
|
|
147
|
+
puts ticker.data
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
#### Private API (with authentication)
|
|
151
|
+
```ruby
|
|
152
|
+
client = KrakenBridge::Client.new(
|
|
153
|
+
api_key: ENV['KRAKEN_API_KEY'],
|
|
154
|
+
api_secret: ENV['KRAKEN_API_SECRET']
|
|
155
|
+
)
|
|
156
|
+
balance = client.account.balance
|
|
157
|
+
puts balance.data
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### WebSocket Real-time Data
|
|
161
|
+
```ruby
|
|
162
|
+
client = KrakenBridge::Client.new
|
|
163
|
+
|
|
164
|
+
client.connect_websocket do |message|
|
|
165
|
+
puts message
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
client.subscribe_ticker(['XBTUSD']) do |data|
|
|
169
|
+
puts "Ticker: #{data}"
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
sleep(30)
|
|
173
|
+
client.disconnect_websocket
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Dependencies
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
# Runtime
|
|
180
|
+
websocket-client-simple ~> 0.8
|
|
181
|
+
|
|
182
|
+
# Development
|
|
183
|
+
bundler ~> 2.0
|
|
184
|
+
rake ~> 13.0
|
|
185
|
+
rspec ~> 3.12
|
|
186
|
+
rubocop ~> 1.50
|
|
187
|
+
rubocop-rspec ~> 2.20
|
|
188
|
+
vcr ~> 6.1
|
|
189
|
+
webmock ~> 3.18
|
|
190
|
+
simplecov ~> 0.22
|
|
191
|
+
yard ~> 0.9
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Publishing Steps
|
|
195
|
+
|
|
196
|
+
1. **Update version** in `lib/kraken_bridge/version.rb`
|
|
197
|
+
```ruby
|
|
198
|
+
VERSION = '1.0.0'
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
2. **Build the gem**
|
|
202
|
+
```bash
|
|
203
|
+
bundle exec rake build
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
3. **Test locally**
|
|
207
|
+
```bash
|
|
208
|
+
bundle exec rspec
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
4. **Push to RubyGems**
|
|
212
|
+
```bash
|
|
213
|
+
gem push pkg/kraken_bridge-1.0.0.gem
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
5. **Update gemspec metadata**
|
|
217
|
+
- Set proper homepage URL
|
|
218
|
+
- Add description
|
|
219
|
+
- Configure allowed_push_host
|
|
220
|
+
|
|
221
|
+
### Current Status
|
|
222
|
+
|
|
223
|
+
✅ **All Features Implemented**
|
|
224
|
+
✅ **Authentication Working**
|
|
225
|
+
✅ **Private APIs Tested**
|
|
226
|
+
✅ **WebSocket Implementation Complete**
|
|
227
|
+
✅ **Documentation Complete**
|
|
228
|
+
✅ **Code Quality**
|
|
229
|
+
✅ **Ready for Publishing**
|
|
230
|
+
|
|
231
|
+
### Next Steps for Publishing
|
|
232
|
+
|
|
233
|
+
1. Update `kraken_bridge.gemspec`:
|
|
234
|
+
```ruby
|
|
235
|
+
spec.summary = "Ruby client for Kraken cryptocurrency exchange API"
|
|
236
|
+
spec.description = "Comprehensive REST and WebSocket integration with Kraken"
|
|
237
|
+
spec.homepage = "https://github.com/balram/kraken_bridge"
|
|
238
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
|
239
|
+
spec.metadata['source_code_uri'] = "https://github.com/balram/kraken_bridge"
|
|
240
|
+
spec.metadata['changelog_uri'] = "https://github.com/balram/kraken_bridge/blob/master/CHANGELOG.md"
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
2. Run final tests
|
|
244
|
+
```bash
|
|
245
|
+
bundle exec rspec
|
|
246
|
+
bundle exec rubocop
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
3. Create git tag
|
|
250
|
+
```bash
|
|
251
|
+
git tag -a v1.0.0 -m "Release version 1.0.0"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
4. Push to RubyGems
|
|
255
|
+
```bash
|
|
256
|
+
gem build kraken_bridge.gemspec
|
|
257
|
+
gem push kraken_bridge-1.0.0.gem
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Key Features Implemented
|
|
261
|
+
|
|
262
|
+
| Feature | Status | Details |
|
|
263
|
+
|---------|--------|---------|
|
|
264
|
+
| REST API (Public) | ✅ | All market data endpoints |
|
|
265
|
+
| REST API (Private) | ✅ | Account & Trading endpoints |
|
|
266
|
+
| WebSocket | ✅ | Ticker, Trades, OHLC, Spread |
|
|
267
|
+
| Authentication | ✅ | HMAC-SHA512 per Kraken spec |
|
|
268
|
+
| Nonce Generation | ✅ | Millisecond precision |
|
|
269
|
+
| 2FA Support | ✅ | OTP parameter support |
|
|
270
|
+
| Rate Limiting | ✅ | Built-in & configurable |
|
|
271
|
+
| Error Handling | ✅ | Custom error classes |
|
|
272
|
+
| Logging | ✅ | Configurable debug logging |
|
|
273
|
+
| Documentation | ✅ | Comprehensive README |
|
|
274
|
+
|
|
275
|
+
### Security Verified
|
|
276
|
+
|
|
277
|
+
- ✅ Proper signature generation (HMAC-SHA512)
|
|
278
|
+
- ✅ Base64 secret decoding
|
|
279
|
+
- ✅ Nonce in milliseconds
|
|
280
|
+
- ✅ No API keys in code
|
|
281
|
+
- ✅ Environment variable support
|
|
282
|
+
- ✅ 2FA-ready implementation
|
|
283
|
+
|
|
284
|
+
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# KrakenBridge
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A Ruby gem for Kraken cryptocurrency exchange APIs. Supports Spot REST workflows for market data and core authenticated trading, plus public WebSocket feeds.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- ✅ **
|
|
7
|
+
- ✅ **Spot REST Coverage for Core Workflows**: Market data, account queries, and trading operations
|
|
8
8
|
- ✅ **WebSocket Real-time Feeds**: Ticker, trades, OHLC/Klines, and spread data
|
|
9
9
|
- ✅ **Automatic Nonce Generation**: Millisecond-precision timestamps
|
|
10
10
|
- ✅ **Signature Generation**: HMAC-SHA512 authentication per Kraken specs
|
|
@@ -179,10 +179,15 @@ client.account.get_export_report(report: 'trades')
|
|
|
179
179
|
|
|
180
180
|
```ruby
|
|
181
181
|
client.trading.add_order(pair: 'XBTUSD', type: 'buy', ordertype: 'limit', volume: 0.01, price: 25000)
|
|
182
|
+
client.trading.amend_order(txid: 'order_id', order_qty: 0.02, limit_price: '26000')
|
|
183
|
+
client.trading.add_order_batch(pair: 'XBTUSD', orders: [{ ordertype: 'limit', type: 'buy', volume: '0.01', price: '25000' }, { ordertype: 'limit', type: 'sell', volume: '0.01', price: '26000' }])
|
|
184
|
+
client.trading.get_websockets_token
|
|
182
185
|
client.trading.edit_order(txid: 'order_id', volume: 0.02, price: 26000)
|
|
183
186
|
client.trading.cancel_order('order_id')
|
|
187
|
+
client.trading.cancel_order(cl_ord_id: 'client-order-id')
|
|
184
188
|
client.trading.cancel_all_orders
|
|
185
189
|
client.trading.cancel_all_orders_after(timeout: 300)
|
|
190
|
+
client.trading.cancel_order_batch(txid: ['order_id_1', 'order_id_2'])
|
|
186
191
|
```
|
|
187
192
|
|
|
188
193
|
### WebSocket (Public Feeds)
|
data/examples/test.rb
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/kraken_bridge'
|
|
4
|
+
|
|
5
|
+
# Initialize client
|
|
6
|
+
client = KrakenBridge::Client.new(
|
|
7
|
+
api_key: ENV.fetch('KRAKEN_API_KEY', nil),
|
|
8
|
+
api_secret: ENV.fetch('KRAKEN_API_SECRET', nil)
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
puts('=' * 80)
|
|
12
|
+
puts 'KRAKEN BRIDGE - API EXAMPLES'
|
|
13
|
+
puts('=' * 80)
|
|
14
|
+
|
|
15
|
+
# PUBLIC API EXAMPLES
|
|
16
|
+
puts "\n\n"
|
|
17
|
+
puts('=' * 80)
|
|
18
|
+
puts 'PUBLIC API EXAMPLES (No Authentication Required)'
|
|
19
|
+
puts('=' * 80)
|
|
20
|
+
|
|
21
|
+
puts "\n1. SERVER TIME"
|
|
22
|
+
puts('-' * 80)
|
|
23
|
+
time_response = client.market_data.time
|
|
24
|
+
puts time_response.data if time_response.success?
|
|
25
|
+
|
|
26
|
+
puts "\n2. SYSTEM STATUS"
|
|
27
|
+
puts('-' * 80)
|
|
28
|
+
status_response = client.market_data.system_status
|
|
29
|
+
puts status_response.data if status_response.success?
|
|
30
|
+
|
|
31
|
+
puts "\n3. ASSET INFORMATION"
|
|
32
|
+
puts('-' * 80)
|
|
33
|
+
assets_response = client.market_data.assets(assets: %w[BTC ETH])
|
|
34
|
+
puts assets_response.data if assets_response.success?
|
|
35
|
+
|
|
36
|
+
puts "\n4. ASSET PAIRS"
|
|
37
|
+
puts('-' * 80)
|
|
38
|
+
pairs_response = client.market_data.asset_pairs(pairs: %w[XBTUSD ETHUSD])
|
|
39
|
+
puts pairs_response.data if pairs_response.success?
|
|
40
|
+
|
|
41
|
+
puts "\n5. TICKER INFORMATION"
|
|
42
|
+
puts('-' * 80)
|
|
43
|
+
ticker_response = client.market_data.ticker(%w[XBTUSD ETHUSD])
|
|
44
|
+
puts ticker_response.data if ticker_response.success?
|
|
45
|
+
|
|
46
|
+
puts "\n6. OHLC DATA"
|
|
47
|
+
puts('-' * 80)
|
|
48
|
+
ohlc_response = client.market_data.ohlc('XBTUSD', interval: 1)
|
|
49
|
+
puts ohlc_response.data if ohlc_response.success?
|
|
50
|
+
|
|
51
|
+
puts "\n7. ORDER BOOK (Depth)"
|
|
52
|
+
puts('-' * 80)
|
|
53
|
+
orderbook_response = client.market_data.order_book('XBTUSD')
|
|
54
|
+
puts orderbook_response.data if orderbook_response.success?
|
|
55
|
+
|
|
56
|
+
puts "\n8. RECENT TRADES"
|
|
57
|
+
puts('-' * 80)
|
|
58
|
+
trades_response = client.market_data.trades('XBTUSD')
|
|
59
|
+
puts trades_response.data if trades_response.success?
|
|
60
|
+
|
|
61
|
+
puts "\n9. SPREAD"
|
|
62
|
+
puts('-' * 80)
|
|
63
|
+
spread_response = client.market_data.spread('XBTUSD')
|
|
64
|
+
puts spread_response.data if spread_response.success?
|
|
65
|
+
|
|
66
|
+
# PRIVATE API EXAMPLES (Requires API Credentials)
|
|
67
|
+
if ENV['KRAKEN_API_KEY'] && ENV['KRAKEN_API_SECRET']
|
|
68
|
+
puts "\n\n"
|
|
69
|
+
puts('=' * 80)
|
|
70
|
+
puts 'PRIVATE API EXAMPLES (Requires Authentication)'
|
|
71
|
+
puts('=' * 80)
|
|
72
|
+
|
|
73
|
+
puts "\n10. ACCOUNT BALANCE"
|
|
74
|
+
puts('-' * 80)
|
|
75
|
+
balance_response = client.account.balance
|
|
76
|
+
puts balance_response.data if balance_response.success?
|
|
77
|
+
puts balance_response.errors unless balance_response.success?
|
|
78
|
+
|
|
79
|
+
puts "\n11. EXTENDED BALANCE"
|
|
80
|
+
puts('-' * 80)
|
|
81
|
+
ext_balance_response = client.account.extended_balance
|
|
82
|
+
puts ext_balance_response.data if ext_balance_response.success?
|
|
83
|
+
|
|
84
|
+
puts "\n12. TRADE BALANCE"
|
|
85
|
+
puts('-' * 80)
|
|
86
|
+
trade_balance_response = client.account.trade_balance
|
|
87
|
+
puts trade_balance_response.data if trade_balance_response.success?
|
|
88
|
+
|
|
89
|
+
puts "\n13. OPEN ORDERS"
|
|
90
|
+
puts('-' * 80)
|
|
91
|
+
open_orders_response = client.account.open_orders
|
|
92
|
+
puts open_orders_response.data if open_orders_response.success?
|
|
93
|
+
|
|
94
|
+
puts "\n14. CLOSED ORDERS"
|
|
95
|
+
puts('-' * 80)
|
|
96
|
+
closed_orders_response = client.account.closed_orders
|
|
97
|
+
puts closed_orders_response.data if closed_orders_response.success?
|
|
98
|
+
|
|
99
|
+
puts "\n15. TRADES HISTORY"
|
|
100
|
+
puts('-' * 80)
|
|
101
|
+
trades_history_response = client.account.trades_history
|
|
102
|
+
puts trades_history_response.data if trades_history_response.success?
|
|
103
|
+
|
|
104
|
+
puts "\n16. OPEN POSITIONS"
|
|
105
|
+
puts('-' * 80)
|
|
106
|
+
positions_response = client.account.open_positions
|
|
107
|
+
puts positions_response.data if positions_response.success?
|
|
108
|
+
|
|
109
|
+
puts "\n17. LEDGER"
|
|
110
|
+
puts('-' * 80)
|
|
111
|
+
ledger_response = client.account.ledger
|
|
112
|
+
puts ledger_response.data if ledger_response.success?
|
|
113
|
+
|
|
114
|
+
puts "\n18. TRADE VOLUME"
|
|
115
|
+
puts('-' * 80)
|
|
116
|
+
volume_response = client.account.trade_volume
|
|
117
|
+
puts volume_response.data if volume_response.success?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
puts "\n\n"
|
|
121
|
+
puts('=' * 80)
|
|
122
|
+
puts 'WEBSOCKET EXAMPLES (Public Feeds)'
|
|
123
|
+
puts('=' * 80)
|
|
124
|
+
|
|
125
|
+
puts "\nWebSocket Features Available:"
|
|
126
|
+
puts '- Subscribe to ticker data'
|
|
127
|
+
puts '- Subscribe to trades'
|
|
128
|
+
puts '- Subscribe to OHLC/Klines'
|
|
129
|
+
puts '- Subscribe to spread data'
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
class Client
|
|
5
|
+
attr_reader :rest, :websocket, :config
|
|
6
|
+
|
|
7
|
+
def initialize(api_key: nil, api_secret: nil, **options)
|
|
8
|
+
@config = KrakenBridge.configuration.dup
|
|
9
|
+
@config.api_key = api_key if api_key
|
|
10
|
+
@config.api_secret = api_secret if api_secret
|
|
11
|
+
|
|
12
|
+
options.each do |key, value|
|
|
13
|
+
@config.send("#{key}=", value) if @config.respond_to?("#{key}=")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@rest = Rest::Client.new(@config)
|
|
17
|
+
@websocket = Websocket::Client.new(@config)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def market_data
|
|
21
|
+
rest.market_data
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def account
|
|
25
|
+
rest.account
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def trading
|
|
29
|
+
rest.trading
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def ticker(pair)
|
|
33
|
+
rest.market_data.ticker(pair)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def trades(pair, since: nil)
|
|
37
|
+
rest.market_data.trades(pair, since: since)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def ohlc(pair, interval: 1, since: nil)
|
|
41
|
+
rest.market_data.ohlc(pair, interval: interval, since: since)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def balance
|
|
45
|
+
rest.account.balance
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def place_order(pair:, type:, order_type:, volume:, **options)
|
|
49
|
+
rest.trading.add_order(
|
|
50
|
+
pair: pair,
|
|
51
|
+
type: type,
|
|
52
|
+
ordertype: order_type,
|
|
53
|
+
volume: volume,
|
|
54
|
+
**options
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def subscribe_ticker(pairs, &)
|
|
59
|
+
websocket.subscribe_ticker(pairs, &)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def subscribe_trades(pairs, &)
|
|
63
|
+
websocket.subscribe_trades(pairs, &)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def subscribe_ohlc(pairs, interval: 1, &block)
|
|
67
|
+
websocket.subscribe_ohlc(pairs, interval: interval, &block)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def subscribe_klines(pairs, interval: 1, &block)
|
|
71
|
+
websocket.subscribe_klines(pairs, interval: interval, &block)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def connect_websocket(&)
|
|
75
|
+
websocket.connect(&)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def disconnect_websocket
|
|
79
|
+
websocket.disconnect
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
class Configuration
|
|
5
|
+
attr_accessor :api_key, :api_secret, :rest_url, :ws_url,
|
|
6
|
+
:timeout, :open_timeout, :logger, :log_level,
|
|
7
|
+
:rate_limit_enabled, :retry_enabled, :max_retries
|
|
8
|
+
|
|
9
|
+
def initialize
|
|
10
|
+
@api_key = ENV.fetch('KRAKEN_API_KEY', nil)
|
|
11
|
+
@api_secret = ENV.fetch('KRAKEN_API_SECRET', nil)
|
|
12
|
+
@rest_url = ENV['KRAKEN_API_URL'] || 'https://api.kraken.com'
|
|
13
|
+
@ws_url = ENV['KRAKEN_WEBSOCKET_URL'] || 'wss://ws.kraken.com'
|
|
14
|
+
@timeout = 30
|
|
15
|
+
@open_timeout = 10
|
|
16
|
+
@log_level = Logger::INFO
|
|
17
|
+
@logger = Middleware::Logger.new(::Logger.new($stdout))
|
|
18
|
+
@rate_limit_enabled = true
|
|
19
|
+
@retry_enabled = true
|
|
20
|
+
@max_retries = 3
|
|
21
|
+
|
|
22
|
+
configure_logger
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def credentials_present?
|
|
26
|
+
!api_key.nil? && !api_secret.nil?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def configure_logger
|
|
32
|
+
@logger.logger.level = @log_level
|
|
33
|
+
@logger.logger.formatter = proc do |severity, datetime, _progname, msg|
|
|
34
|
+
"[#{datetime.strftime('%Y-%m-%d %H:%M:%S')}] #{severity} -- #{msg}\n"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
module Errors
|
|
5
|
+
class ApiError < BaseError
|
|
6
|
+
attr_reader :error_codes, :response
|
|
7
|
+
|
|
8
|
+
def initialize(message, error_codes = [], response = nil)
|
|
9
|
+
@error_codes = error_codes
|
|
10
|
+
@response = response
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def error_messages
|
|
15
|
+
error_codes.join(', ')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
module Errors
|
|
5
|
+
class BaseError < StandardError
|
|
6
|
+
attr_reader :original_error
|
|
7
|
+
|
|
8
|
+
def initialize(message = nil, original_error = nil)
|
|
9
|
+
@original_error = original_error
|
|
10
|
+
super(message)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
module Errors
|
|
5
|
+
class RateLimitError < BaseError
|
|
6
|
+
attr_reader :retry_after
|
|
7
|
+
|
|
8
|
+
def initialize(message, retry_after = nil)
|
|
9
|
+
@retry_after = retry_after
|
|
10
|
+
super(message)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module KrakenBridge
|
|
4
|
+
module Errors
|
|
5
|
+
class ValidationError < BaseError
|
|
6
|
+
attr_reader :field, :value
|
|
7
|
+
|
|
8
|
+
def initialize(message, field: nil, value: nil)
|
|
9
|
+
@field = field
|
|
10
|
+
@value = value
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|