poloniex.rb 0.0.0 → 0.1.1

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.
@@ -10,66 +10,135 @@ require 'openssl'
10
10
 
11
11
  require_relative '../Configuration'
12
12
  require_relative '../Error'
13
+ require_relative '../V1'
14
+ require_relative '../../Hash/x_www_form_urlencode'
15
+ require_relative '../../Object/inQ'
13
16
 
14
17
  module Poloniex
15
18
  module V1
16
19
  class Client
17
20
 
18
- API_HOST = 'api.poloniex.com'
21
+ class << self
22
+ def path_prefix
23
+ ''
24
+ end
25
+ end
19
26
 
20
- # Get candles for specific symbol
21
- def candles(symbol, interval: 'DAY_1', start_time: nil, end_time: nil, limit: nil)
22
- args = {interval: interval, startTime: start_time, endTime: end_time, limit: limit}
23
- response = get(path: "/markets/#{symbol}/candles", args: args)
27
+ # Public endpoints
28
+
29
+ ## Reference Data
30
+
31
+ ### Symbol Information
32
+ ### GET https://api.poloniex.com/markets
33
+ ### GET https://api.poloniex.com/markets/{symbol}
34
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#symbol-information
35
+ def markets(symbol = nil)
36
+ path = "/markets"
37
+ path += "/#{symbol}"if symbol
38
+ response = get(path: path)
24
39
  handle_response(response)
25
40
  end
26
41
 
27
- # Get all currencies if no currency specified.
28
- def currencies(currency = nil)
29
- path = "/currencies"
42
+ ### Currency Information
43
+ ### GET https://api.poloniex.com/currencies
44
+ ### GET https://api.poloniex.com/currencies/{currency}
45
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#currency-information
46
+ def currencies(currency = nil, include_multichain_currencies: nil)
47
+ args = {}
48
+ args.merge!(includeMultiChainCurrencies: include_multichain_currencies) if include_multichain_currencies
49
+ path = '/currencies'
30
50
  path += "/#{currency}" if currency
51
+ response = get(path: path, args: args)
52
+ handle_response(response)
53
+ end
54
+
55
+ ### System Timestamp
56
+ ### GET https://api.poloniex.com/timestamp
57
+ ### GET https://api.poloniex.com/timestamp
58
+ ### https://api-docs.poloniex.com/spot/api/public/reference-data#system-timestamp
59
+ def timestamp
60
+ response = get(path: '/timestamp')
61
+ handle_response(response)
62
+ end
63
+
64
+ ## Market Data
65
+
66
+ ### Prices
67
+ ### GET https://api.poloniex.com/markets/price
68
+ ### GET https://api.poloniex.com/markets/{symbol}/price
69
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#prices
70
+ def price(symbol = nil)
71
+ path = '/markets/price'
72
+ path = "/markets/#{symbol}/price" if symbol
31
73
  response = get(path: path)
32
74
  handle_response(response)
33
75
  end
34
76
 
35
- # Get mark prices for all symbols if no symbol specified.
36
- def mark_prices(symbol = nil)
77
+ ### Mark Price
78
+ ### GET https://api.poloniex.com/markets/markPrice
79
+ ### GET https://api.poloniex.com/markets/{symbol}/markPrice
80
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#mark-price
81
+ def mark_price(symbol = nil)
37
82
  path = '/markets/markPrice'
38
83
  path = "/markets/#{symbol}/markPrice" if symbol
39
84
  response = get(path: path)
40
85
  handle_response(response)
41
86
  end
42
87
 
43
- # Get mark price components for specific symbol
88
+ ### Mark Price Components
89
+ ### GET https://api.poloniex.com/markets/{symbol}/markPriceComponents
90
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#mark-price-components
44
91
  def mark_price_components(symbol)
45
92
  response = get(path: "/markets/#{symbol}/markPriceComponents")
46
93
  handle_response(response)
47
94
  end
48
95
 
49
- # Get all markets/symbols if no symbol specified.
50
- def markets(symbol = nil)
51
- path = "/markets"
52
- path += "/#{symbol}"if symbol
53
- response = get(path: path)
96
+ ### Order Book
97
+ ### GET https://api.poloniex.com/markets/{symbol}/orderBook
98
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#order-book
99
+ def order_book(symbol, scale: nil, limit: nil)
100
+ response = get(
101
+ path: "/markets/#{symbol}/orderBook",
102
+ args: {
103
+ scale: scale,
104
+ limit: limit
105
+ }
106
+ )
54
107
  handle_response(response)
55
108
  end
56
109
 
57
- # Get order book for specific symbol
58
- def order_book(symbol, scale: nil, limit: nil)
59
- args = {scale: scale, limit: limit}
60
- response = get(path: "/markets/#{symbol}/orderBook", args: args)
110
+ ### Candles
111
+ ### GET https://api.poloniex.com/markets/{symbol}/candles
112
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#candles
113
+ ### interval: The unit of time by which to aggregate data. Valid values are: MINUTE_1, MINUTE_5, MINUTE_10, MINUTE_15, MINUTE_30, HOUR_1, HOUR_2, HOUR_4, HOUR_6, HOUR_12, DAY_1, DAY_3, WEEK_1 and MONTH_1.
114
+ def candles(symbol, interval: 'DAY_1', start_time: nil, end_time: nil, limit: nil)
115
+ response = get(
116
+ path: "/markets/#{symbol}/candles",
117
+ args: {
118
+ interval: interval,
119
+ startTime: start_time,
120
+ endTime: end_time,
121
+ limit: limit
122
+ }
123
+ )
61
124
  handle_response(response)
62
125
  end
63
126
 
64
- # Get prices for all symbols if no symbol specified.
65
- def prices(symbol = nil)
66
- path = '/markets/price'
67
- path = "/markets/#{symbol}/price" if symbol
68
- response = get(path: path)
127
+ ### Trades
128
+ ### GET https://api.poloniex.com/markets/{symbol}/trades
129
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#trades
130
+ def trades(symbol, limit: nil)
131
+ response = get(
132
+ path: "/markets/#{symbol}/trades",
133
+ args: {limit: limit}
134
+ )
69
135
  handle_response(response)
70
136
  end
71
137
 
72
- # Get 24h ticker for all symbols if no symbol specified.
138
+ ### Ticker
139
+ ### GET https://api.poloniex.com/markets/ticker24h
140
+ ### GET https://api.poloniex.com/markets/{symbol}/ticker24h
141
+ ### https://api-docs.poloniex.com/spot/api/public/market-data#ticker
73
142
  def ticker_24h(symbol = nil)
74
143
  path = '/markets/ticker24h'
75
144
  path = "/markets/#{symbol}/ticker24h" if symbol
@@ -77,16 +146,369 @@ module Poloniex
77
146
  handle_response(response)
78
147
  end
79
148
 
80
- # Get server timestamp
81
- def timestamp
82
- response = get(path: '/timestamp')
149
+ ## Margin
150
+
151
+ ### Collateral Info
152
+ ### GET https://api.poloniex.com/markets/collateralInfo
153
+ ### GET https://api.poloniex.com/markets/{currency}/collateralInfo
154
+ ### https://api-docs.poloniex.com/spot/api/public/margin#collateral-info
155
+ def collateral_info(currency = nil)
156
+ path = '/markets/collateralInfo'
157
+ path = "/markets/#{currency}/collateralInfo" if currency
158
+ response = get(path: path)
83
159
  handle_response(response)
84
160
  end
85
161
 
86
- # Get trades for specific symbol
87
- def trades(symbol, limit: nil)
88
- args = {limit: limit}
89
- response = get(path: "/markets/#{symbol}/trades", args: args)
162
+ ### Borrow Rates Info
163
+ ### GET https://api.poloniex.com/markets/borrowRatesInfo
164
+ ### https://api-docs.poloniex.com/spot/api/public/margin#borrow-rates-info
165
+ def borrow_rates_info
166
+ response = get(path: '/markets/borrowRatesInfo')
167
+ handle_response(response)
168
+ end
169
+
170
+ # Authenticated endpoints
171
+
172
+ ## Account
173
+
174
+ ### Account Information
175
+ ### GET https://api.poloniex.com/accounts
176
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
177
+ def accounts
178
+ response = get(path: '/accounts')
179
+ handle_response(response)
180
+ end
181
+
182
+ ### All Account Balances
183
+ ### GET https://api.poloniex.com/accounts/balances
184
+ ### GET https://api.poloniex.com/accounts/{id}/balances
185
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
186
+ def accounts_balances(account_id: nil, account_type: nil)
187
+ path = '/accounts/balances'
188
+ path = "/accounts/#{account_id}/balances" if account_id
189
+ response = get(path: path, args: {accountType: account_type})
190
+ handle_response(response)
191
+ end
192
+
193
+ ### Account Activity
194
+ ### GET https://api.poloniex.com/accounts/activity
195
+ ### https://api-docs.poloniex.com/spot/api/private/account#account-activity
196
+ def accounts_activity(start_time: nil, end_time: nil, acivity_type: nil, limit: nil, from: nil, direction: nil, currency: nil)
197
+ response = get(
198
+ path: '/accounts/activity',
199
+ args: {
200
+ startTime: start_time,
201
+ endTime: end_time,
202
+ acivityType: acivity_type,
203
+ limit: limit,
204
+ from: from,
205
+ direction: direction,
206
+ currency: currency
207
+ }
208
+ )
209
+ handle_response(response)
210
+ end
211
+
212
+ ### Accounts Transfer
213
+ ### POST https://api.poloniex.com/accounts/transfer
214
+ ### https://api-docs.poloniex.com/spot/api/private/account#accounts-transfer
215
+ def accounts_transfer(currency:, amount:, from_account:, to_account:)
216
+ response = post(
217
+ path: '/accounts/transfer',
218
+ args: {
219
+ currency: currency,
220
+ amount: amount,
221
+ fromAccount: from_account,
222
+ toAccount: to_account
223
+ }
224
+ )
225
+ handle_response(response)
226
+ end
227
+
228
+ ### Accounts Transfer Records
229
+ ### GET https://api.poloniex.com/accounts/transfer
230
+ ### GET https://api.poloniex.com/accounts/transfer/{id}
231
+ ### https://api-docs.poloniex.com/spot/api/private/account#accounts-transfer-records
232
+ def accounts_transfer_records(transfer_id: nil, limit: nil, from: nil, direction: nil, currency: nil, start_time: nil, end_time: nil)
233
+ path = '/accounts/transfer'
234
+ path += "/#{transfer_id}" if transfer_id
235
+ response = get(
236
+ path: path,
237
+ args: {
238
+ limit: limit,
239
+ from: from,
240
+ direction: direction,
241
+ currency: currency,
242
+ startTime: start_time,
243
+ endTime: end_time
244
+ }
245
+ )
246
+ handle_response(response)
247
+ end
248
+
249
+ ### Fee Info
250
+ ### GET https://api.poloniex.com/feeinfo
251
+ ### https://api-docs.poloniex.com/spot/api/private/account#fee-info
252
+ ### Should it be called accounts_fee_info?
253
+ def fee_info
254
+ response = get(path: '/feeinfo')
255
+ handle_response(response)
256
+ end
257
+
258
+ ### Interest History
259
+ ### GET https://api.poloniex.com/accounts/interest/history
260
+ ### https://api-docs.poloniex.com/spot/api/private/account#interest-history
261
+ def accounts_interest_history(limit: nil, from: nil, direction: nil, start_time: nil, end_time: nil)
262
+ response = get(
263
+ path: '/accounts/interest/history',
264
+ args: {
265
+ limit: limit,
266
+ from: from,
267
+ direction: direction,
268
+ startTime: start_time,
269
+ endTime: end_time
270
+ }
271
+ )
272
+ handle_response(response)
273
+ end
274
+
275
+ ## Wallet
276
+
277
+ ### Deposit Addresses
278
+ ### GET https://api.poloniex.com/wallets/addresses
279
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#deposit-addresses
280
+ def deposit_addresses(currency = nil)
281
+ response = get(path: '/wallets/addresses', args: {currency: currency})
282
+ handle_response(response)
283
+ end
284
+
285
+ ### Wallets Activity Records
286
+ ### GET https://api.poloniex.com/wallets/activity
287
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#wallets-activity-records
288
+ def wallets_activity(start:, finish:, activity_type: nil)
289
+ response = get(
290
+ path: '/wallets/activity',
291
+ args: {
292
+ start: start,
293
+ end: finish,
294
+ activityType: activity_type
295
+ }
296
+ )
297
+ handle_response(response)
298
+ end
299
+
300
+ ### New Currency Address
301
+ ### POST https://api.poloniex.com/wallets/address
302
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#new-currency-address
303
+ def new_currency_address(currency)
304
+ response = post(path: '/wallets/address', args: {currency: currency})
305
+ handle_response(response)
306
+ end
307
+
308
+ ### Withdraw Currency
309
+ ### POST https://api.poloniex.com/wallets/withdraw
310
+ ### https://api-docs.poloniex.com/spot/api/private/wallet#withdraw-currency
311
+ def withdraw_currency(currency:, amount:, address:, payment_id: nil, allow_borrow: nil)
312
+ response = post(
313
+ path: '/wallets/withdraw',
314
+ args: {
315
+ currency: currency,
316
+ amount: amount,
317
+ address: address,
318
+ paymentId: payment_id,
319
+ allowBorrow: allow_borrow
320
+ }
321
+ )
322
+ handle_response(response)
323
+ end
324
+
325
+ ## Margin
326
+
327
+ ### Account Margin
328
+ ### GET https://api.poloniex.com/margin/accountMargin
329
+ ### https://api-docs.poloniex.com/spot/api/private/margin#account-margin
330
+ ### account_type: Currently only SPOT is supported.
331
+ def account_margin(account_type: 'SPOT')
332
+ response = get(path: '/margin/accountMargin', args: {accountType: account_type})
333
+ handle_response(response)
334
+ end
335
+
336
+ ### Borrow Status
337
+ ### GET https://api.poloniex.com/margin/borrowStatus
338
+ ### https://api-docs.poloniex.com/spot/api/private/margin#borrow-status
339
+ def borrow_status(currency: nil)
340
+ response = get(path: '/margin/borrowStatus', args: {currency: currency})
341
+ handle_response(response)
342
+ end
343
+
344
+ ### Maximum Buy/Sell Amount
345
+ ### GET https://api.poloniex.com/margin/maxSize
346
+ ### https://api-docs.poloniex.com/spot/api/private/margin#maximum-buysell-amount
347
+ def max_buy_sell_amount(symbol)
348
+ response = get(path: '/margin/maxSize', args: {symbol: symbol})
349
+ handle_response(response)
350
+ end
351
+
352
+ ## Orders
353
+
354
+ ### Create Order
355
+ ### POST https://api.poloniex.com/orders
356
+ ### https://api-docs.poloniex.com/spot/api/private/order#create-order
357
+ def create_order(
358
+ symbol:,
359
+ side:,
360
+ time_in_force: nil,
361
+ type: nil,
362
+ account_type: nil,
363
+ price: nil,
364
+ quantity: nil,
365
+ amount: nil,
366
+ client_order_id: nil,
367
+ allow_borrow: nil,
368
+ stp_mode: nil,
369
+ slippage_tolerance: nil
370
+ )
371
+ response = post(
372
+ path: '/orders',
373
+ args: {
374
+ symbol: symbol,
375
+ side: side,
376
+ timeInForce: time_in_force,
377
+ type: type,
378
+ accountType: account_type,
379
+ price: price,
380
+ quantity: quantity,
381
+ amount: amount,
382
+ clientOrderId: client_order_id,
383
+ allowBorrow: allow_borrow,
384
+ stpMode: stp_mode,
385
+ slippageTolerance: slippage_tolerance,
386
+ }
387
+ )
388
+ handle_response(response)
389
+ end
390
+
391
+ ### Create Multiple Orders
392
+ ### POST https://api.poloniex.com/orders/batch
393
+ ### https://api-docs.poloniex.com/spot/api/private/order#create-multiple-orders
394
+ def create_multiple_orders(orders)
395
+ response = post(path: '/orders/batch', args: orders)
396
+ handle_response(response)
397
+ end
398
+
399
+ ### Cancel Replace Order
400
+ ### PUT https://api.poloniex.com/orders/{id}
401
+ ### PUT https://api.poloniex.com/orders/cid:{clientOrderId}
402
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-replace-order
403
+ def cancel_replace_order(
404
+ order_id: nil,
405
+ client_order_id: false,
406
+ price: nil,
407
+ quantity: nil,
408
+ amount: nil,
409
+ type: nil,
410
+ time_in_force: nil,
411
+ allow_borrow: nil,
412
+ proceed_on_failure: nil,
413
+ slippage_tolerance: nil
414
+ )
415
+ path = "/orders/#{order_id}"
416
+ path = "/orders/cid:#{order_id}" if client_order_id
417
+ args = {
418
+ price: price,
419
+ quantity: quantity,
420
+ amount: amount,
421
+ type: type,
422
+ timeInForce: time_in_force,
423
+ allowBorrow: allow_borrow,
424
+ proceedOnFailure: proceed_on_failure,
425
+ slippageTolerance: slippage_tolerance,
426
+ }
427
+ args.merge!(clientOrderId: order_id) if client_order_id
428
+ response = put(path: path, args: args)
429
+ handle_response(response)
430
+ end
431
+
432
+ ### Open Orders
433
+ ### GET https://api.poloniex.com/orders
434
+ ### https://api-docs.poloniex.com/spot/api/private/order#open-orders
435
+ def open_orders(symbol: nil, side: nil, from: nil, direction: nil, limit: nil)
436
+ response = get(
437
+ path: '/orders',
438
+ args: {
439
+ symbol: symbol,
440
+ side: side,
441
+ from: from,
442
+ direction: direction,
443
+ limit: limit
444
+ }
445
+ )
446
+ handle_response(response)
447
+ end
448
+
449
+ ### Order Details
450
+ ### GET https://api.poloniex.com/orders/{id}
451
+ ### GET https://api.poloniex.com/orders/cid:{clientOrderId}
452
+ ### https://api-docs.poloniex.com/spot/api/private/order#order-details
453
+ def order_details(order_id:, client_order_id: false)
454
+ path = "/orders/#{order_id}"
455
+ path = "/orders/cid:#{order_id}" if client_order_id
456
+ response = get(path: path, args: {id: order_id})
457
+ handle_response(response)
458
+ end
459
+
460
+ ### Cancel Order by Id
461
+ ### DELETE https://api.poloniex.com/orders/{id}
462
+ ### DELETE https://api.poloniex.com/orders/cid:{clientOrderId}
463
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-order-by-id
464
+ def cancel_order(order_id:, client_order_id: false)
465
+ path = "/orders/#{order_id}"
466
+ path = "/orders/cid:#{order_id}" if client_order_id
467
+ response = delete(path: path, args: {id: order_id})
468
+ handle_response(response)
469
+ end
470
+
471
+ ### Cancel Multiple Orders by Ids
472
+ ### DELETE https://api.poloniex.com/orders/cancelByIds
473
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-multiple-orders-by-ids
474
+ def cancel_multiple_orders(order_ids: nil, client_order_ids: nil)
475
+ response = delete(
476
+ path: '/orders/cancelByIds',
477
+ args: {
478
+ orderIds: order_ids,
479
+ clientOrderIds: client_order_ids
480
+ }
481
+ )
482
+ handle_response(response)
483
+ end
484
+
485
+ ### Cancel All Orders
486
+ ### DELETE https://api.poloniex.com/orders
487
+ ### https://api-docs.poloniex.com/spot/api/private/order#cancel-all-orders
488
+ def cancel_all_orders(symbols: nil, account_types: nil)
489
+ response = delete(
490
+ path: '/orders',
491
+ args: {
492
+ symbols: symbols,
493
+ accountTypes: account_types
494
+ }
495
+ )
496
+ handle_response(response)
497
+ end
498
+
499
+ ### Kill Switch
500
+ ### POST https://api.poloniex.com/orders/killSwitch
501
+ ### https://api-docs.poloniex.com/spot/api/private/order#kill-switch
502
+ def kill_switch(timeout:)
503
+ response = post(path: '/orders/killSwitch', args: {timeout: timeout})
504
+ handle_response(response)
505
+ end
506
+
507
+ ### Kill Switch Status
508
+ ### GET https://api.poloniex.com/orders/killSwitchStatus
509
+ ### https://api-docs.poloniex.com/spot/api/private/order#kill-switch-status
510
+ def kill_switch_status
511
+ response = get(path: '/orders/killSwitchStatus')
90
512
  handle_response(response)
91
513
  end
92
514
 
@@ -101,58 +523,109 @@ module Poloniex
101
523
  def initialize(api_key: nil, api_secret: nil, debug: nil, logger: nil, configuration: nil)
102
524
  @api_key = api_key || configuration&.api_key || Poloniex.configuration.api_key
103
525
  @api_secret = api_secret || configuration&.api_secret || Poloniex.configuration.api_secret
104
- @debug = debug || configuration&.debug || Poloniex.configuration.debug
526
+ @debug = !![debug, configuration&.debug, Poloniex.configuration.debug].compact.first
105
527
  @logger = logger || configuration&.logger || Poloniex.configuration.logger
106
528
  end
107
529
 
108
- def request_timestamp
109
- @request_timestamp ||= (Time.now.to_i * 1000).to_s
530
+ def get(path:, args: {})
531
+ do_request(verb: 'GET', path: path, args: args)
110
532
  end
111
533
 
112
- def message(verb:, path:, args:)
113
- case verb
114
- when 'GET'
115
- if args.empty?
116
- [request_timestamp, verb, path].join
534
+ def post(path:, args: {})
535
+ do_request(verb: 'POST', path: path, args: args)
536
+ end
537
+
538
+ def delete(path:, args: {})
539
+ do_request(verb: 'DELETE', path: path, args: args)
540
+ end
541
+
542
+ def put(path:, args: {})
543
+ do_request(verb: 'PUT', path: path, args: args)
544
+ end
545
+
546
+ def do_request(verb:, path:, args: {})
547
+ verb = verb.to_s.upcase
548
+ raise ArgumentError, "Unsupported HTTP method: #{verb}" unless verb.in?(Poloniex::Client::ALLOWABLE_VERBS)
549
+ request_string = request_string(path:)
550
+ args = args(verb: verb, path: path, supplied_args: args)
551
+ headers = headers(verb: verb, path: path, args: args)
552
+ log_request(verb: verb, request_string: request_string, args: args, headers: headers) if use_logging?
553
+ response =
554
+ if verb.in?(%w{POST PUT}) && args.is_a?(Hash) && (args.key?(:body) || args.key?('body'))
555
+ raw_body = args[:body] || args['body']
556
+ HTTP.send(verb.downcase, request_string, raw_body, headers)
117
557
  else
118
- query_string = args.x_www_form_urlencode
119
- [request_timestamp, verb, path, '?', query_string].join
558
+ HTTP.send(verb.downcase, request_string, args, headers)
120
559
  end
560
+ @request_timestamp = nil
561
+ response
562
+ end
563
+
564
+ def request_string(path:)
565
+ "https://#{Poloniex::Client::API_HOST}#{self.class.path_prefix}#{path}"
566
+ end
567
+
568
+ def args(verb:, path:, supplied_args:)
569
+ return supplied_args if supplied_args.is_a?(Array) # create_multiple_orders() supplies an array.
570
+ args = supplied_args.reject{|_, v| v.nil?}.transform_keys(&:to_s).transform_values(&:to_s)
571
+ if verb.in?(%w{POST PUT})
572
+ return { body: '' } if args.empty?
573
+ return { body: request_body(args) }
121
574
  end
575
+ if verb.in?(%w{GET DELETE}) && use_auth?(path)
576
+ args.merge!("signTimestamp" => request_timestamp.to_s)
577
+ end
578
+ args
122
579
  end
123
580
 
124
- def signature(message)
125
- # digest = OpenSSL::Digest.new('SHA256')
126
- # hmac = OpenSSL::HMAC.digest(digest, @api_secret, message)
127
- # Base64.strict_encode64(hmac)
128
- OpenSSL::HMAC.hexdigest(
129
- 'sha256',
130
- @api_secret,
131
- message
132
- )
581
+ def headers(verb:, path:, args:)
582
+ headers = {}
583
+ if use_auth?(path)
584
+ if verb.in?(%w{POST PUT})
585
+ if args.is_a?(Hash) && (args.key?(:body) || args.key?('body'))
586
+ body_str = args[:body] || args['body']
587
+ headers["Content-Type"] = "application/json; charset=UTF-8" unless body_str.to_s.empty?
588
+ else
589
+ headers["Content-Type"] = "application/json; charset=UTF-8"
590
+ end
591
+ end
592
+ headers.merge!(
593
+ 'key' => @api_key,
594
+ 'signature' => signature(verb: verb, path: path, args: args),
595
+ 'signTimestamp' => request_timestamp.to_s
596
+ )
597
+ end
598
+ headers
133
599
  end
134
600
 
135
- def request_string(path)
136
- "https://#{API_HOST}#{path}"
601
+ def request_timestamp
602
+ @request_timestamp ||= (Time.now.to_f * 1000).to_i
137
603
  end
138
604
 
139
- def headers(signature)
140
- {
141
- 'Content-Type' => 'application/json',
142
- 'key' => @api_key,
143
- 'signatureMethod' => 'HmacSHA256',
144
- 'signature' => signature,
145
- 'signatureVersion' => '2',
146
- 'timestamp' => request_timestamp
147
- }
605
+ def signature_message(verb:, path:, args:)
606
+ case verb
607
+ when 'GET', 'DELETE'
608
+ args_for_signing = args.merge("signTimestamp" => request_timestamp.to_s).transform_values(&:to_s)
609
+ signed_args = args_for_signing.sort_by{|k, _| k}.to_h.x_www_form_urlencode
610
+ when 'POST', 'PUT'
611
+ body_str = request_body(args)
612
+ signed_args = "requestBody=#{body_str}&signTimestamp=#{request_timestamp}"
613
+ end
614
+ "#{verb}\n#{path}\n#{signed_args}"
148
615
  end
149
616
 
150
- def use_logging?
151
- !@logger.nil?
617
+ def request_body(args)
618
+ return '' if args.nil?
619
+ if args.respond_to?(:keys) && (args.key?(:body) || args.key?('body'))
620
+ return args[:body] || args['body']
621
+ end
622
+ return '' if args.empty?
623
+ JSON.generate(args, separators: [',', ':'])
152
624
  end
153
625
 
154
- def log_args?(args)
155
- !args.values.all?(&:nil?)
626
+ def signature(verb:, path:, args:)
627
+ digest = OpenSSL::HMAC.digest("sha256", @api_secret, signature_message(verb: verb, path: path, args: args).encode("utf-8"))
628
+ Base64.strict_encode64(digest)
156
629
  end
157
630
 
158
631
  def log_request(verb:, request_string:, args:, headers:)
@@ -178,24 +651,15 @@ module Poloniex
178
651
  @logger.error(log_string)
179
652
  end
180
653
 
181
- def do_request(verb:, path:, args: {})
182
- sorted_args = args.reject{|_, v| v.nil?}.sort.to_h
183
- message = message(verb: verb, path: path, args: sorted_args)
184
- signature = signature(message)
185
- headers = headers(signature)
186
- log_request(verb: verb, request_string: request_string(path), args: sorted_args, headers: headers) if use_logging?
187
- @request_timestamp = nil
188
- HTTP.send(verb.to_s.downcase, request_string(path), sorted_args, headers)
189
- end
190
-
191
- def get(path:, args: {})
192
- do_request(verb: 'GET', path: path, args: args)
193
- end
194
-
195
654
  def handle_response(response)
196
655
  if response.success?
197
- parsed_body = JSON.parse(response.body)
198
- log_response(code: response.code, message: response.message, body: response.body) if use_logging?
656
+ body = response.body.to_s
657
+ if body.strip.empty?
658
+ log_response(code: response.code, message: response.message, body: body) if use_logging?
659
+ return {}
660
+ end
661
+ parsed_body = JSON.parse(body)
662
+ log_response(code: response.code, message: response.message, body: body) if use_logging?
199
663
  parsed_body
200
664
  else
201
665
  case response.code.to_i
@@ -204,32 +668,51 @@ module Poloniex
204
668
  raise Poloniex::InvalidRequestError.new(
205
669
  code: response.code,
206
670
  message: response.message,
207
- body: body
671
+ body: response.body
208
672
  )
209
673
  when 401
210
674
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
211
675
  raise Poloniex::AuthenticationError.new(
212
676
  code: response.code,
213
677
  message: response.message,
214
- body: body
678
+ body: response.body
215
679
  )
216
680
  when 429
217
681
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
218
682
  raise Poloniex::RateLimitError.new(
219
683
  code: response.code,
220
684
  message: response.message,
221
- body: body
685
+ body: response.body
222
686
  )
223
687
  else
224
688
  log_error(code: response.code, message: response.message, body: response.body) if use_logging?
225
689
  raise Poloniex::APIError.new(
226
690
  code: response.code,
227
691
  message: response.message,
228
- body: body
692
+ body: response.body
229
693
  )
230
694
  end
231
695
  end
232
696
  end
697
+
698
+ def log_args?(args)
699
+ return false if args.nil?
700
+ return !args.values.all?(&:nil?) if args.respond_to?(:values) # Hash-like
701
+ return !args.empty? if args.respond_to?(:empty?) # Array-like or others
702
+ true
703
+ end
704
+
705
+ def public_path?(path)
706
+ Poloniex::V1::PUBLIC_PATH_PREFIXES.any?{|prefix| path.start_with?(prefix)}
707
+ end
708
+
709
+ def use_auth?(path)
710
+ !public_path?(path)
711
+ end
712
+
713
+ def use_logging?
714
+ !@logger.nil?
715
+ end
233
716
  end
234
717
  end
235
718
  end