cryptum 0.0.380 → 0.0.381
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/.rubocop_todo.yml +2 -13
- data/bin/cryptum +1 -1
- data/bin/cryptum-forecast +3 -3
- data/lib/cryptum/api/exchange_rates.rb +42 -0
- data/lib/cryptum/api/fees.rb +36 -0
- data/lib/cryptum/api/order_history.rb +56 -0
- data/lib/cryptum/api/orders.rb +253 -0
- data/lib/cryptum/api/portfolio.rb +79 -0
- data/lib/cryptum/api/products.rb +65 -0
- data/lib/cryptum/api/rest.rb +177 -0
- data/lib/cryptum/api/signature.rb +79 -0
- data/lib/cryptum/api.rb +9 -659
- data/lib/cryptum/event/buy.rb +1 -1
- data/lib/cryptum/event/cancel.rb +1 -1
- data/lib/cryptum/event/gtfo.rb +1 -1
- data/lib/cryptum/event/sell.rb +1 -1
- data/lib/cryptum/order_book/generate.rb +1 -1
- data/lib/cryptum/portfolio/balance.rb +3 -5
- data/lib/cryptum/ui/order_execution.rb +2 -2
- data/lib/cryptum/version.rb +1 -1
- data/lib/cryptum/web_sock/coinbase.rb +1 -1
- data/spec/lib/cryptum/api/exchange_rates_spec.rb +10 -0
- data/spec/lib/cryptum/api/fees_spec.rb +10 -0
- data/spec/lib/cryptum/api/order_history_spec.rb +10 -0
- data/spec/lib/cryptum/api/orders_spec.rb +10 -0
- data/spec/lib/cryptum/api/portfolio_spec.rb +10 -0
- data/spec/lib/cryptum/api/products_spec.rb +10 -0
- data/spec/lib/cryptum/api/rest_spec.rb +10 -0
- data/spec/lib/cryptum/api/signature_spec.rb +10 -0
- metadata +17 -1
data/lib/cryptum/api.rb
CHANGED
@@ -9,604 +9,14 @@ require 'rest-client'
|
|
9
9
|
module Cryptum
|
10
10
|
# This plugin is used to interact withbtje Coinbase REST API
|
11
11
|
module API
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
:GET
|
21
|
-
else
|
22
|
-
opts[:http_method].to_s.upcase.scrub.strip.chomp.to_sym
|
23
|
-
end
|
24
|
-
|
25
|
-
api_call = opts[:api_call].to_s.scrub.strip.chomp
|
26
|
-
api_call = '/users/self/verify' if opts[:api_call].nil?
|
27
|
-
|
28
|
-
if opts[:params].nil?
|
29
|
-
path = api_call
|
30
|
-
else
|
31
|
-
uri = Addressable::URI.new
|
32
|
-
uri.query_values = opts[:params]
|
33
|
-
params = uri.query
|
34
|
-
path = "#{api_call}?#{params}"
|
35
|
-
end
|
36
|
-
|
37
|
-
http_body = opts[:http_body].to_s.scrub.strip.chomp
|
38
|
-
|
39
|
-
api_timestamp = Time.now.utc.to_i.to_s
|
40
|
-
|
41
|
-
api_signature = Base64.strict_encode64(
|
42
|
-
OpenSSL::HMAC.digest(
|
43
|
-
'sha256',
|
44
|
-
Base64.strict_decode64(api_secret),
|
45
|
-
"#{api_timestamp}#{http_method}#{path}#{http_body}"
|
46
|
-
)
|
47
|
-
)
|
48
|
-
|
49
|
-
if http_body == ''
|
50
|
-
api_signature = Base64.strict_encode64(
|
51
|
-
OpenSSL::HMAC.digest(
|
52
|
-
'sha256',
|
53
|
-
Base64.strict_decode64(api_secret),
|
54
|
-
"#{api_timestamp}#{http_method}#{path}"
|
55
|
-
)
|
56
|
-
)
|
57
|
-
end
|
58
|
-
|
59
|
-
api_signature_response = {}
|
60
|
-
api_signature_response[:api_timestamp] = api_timestamp
|
61
|
-
api_signature_response[:api_signature] = api_signature
|
62
|
-
|
63
|
-
api_signature_response
|
64
|
-
rescue RestClient::ExceptionWithResponse => e
|
65
|
-
File.open('/tmp/cryptum-errors.txt', 'a') do |f|
|
66
|
-
f.puts Time.now.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
67
|
-
f.puts "Module: #{self}"
|
68
|
-
f.puts "URL: #{api_endpoint}#{api_call}"
|
69
|
-
f.puts "PARAMS: #{params.inspect}"
|
70
|
-
f.puts "HTTP POST BODY: #{http_body.inspect}" if http_body != ''
|
71
|
-
f.puts "#{e}\n#{e.response}\n\n\n"
|
72
|
-
end
|
73
|
-
rescue StandardError => e
|
74
|
-
raise e
|
75
|
-
end
|
76
|
-
|
77
|
-
private_class_method def self.rest_api_call(opts = {})
|
78
|
-
env = opts[:env]
|
79
|
-
option_choice = opts[:option_choice]
|
80
|
-
order_type = opts[:order_type]
|
81
|
-
event_notes = opts[:event_notes]
|
82
|
-
api_endpoint = opts[:api_endpoint]
|
83
|
-
base_increment = opts[:base_increment].to_f
|
84
|
-
|
85
|
-
api_key = env[:api_key]
|
86
|
-
api_secret = env[:api_secret]
|
87
|
-
api_passphrase = env[:api_passphrase]
|
88
|
-
api_endpoint = 'https://api.exchange.coinbase.com'
|
89
|
-
api_endpoint = 'https://api-public.sandbox.exchange.coinbase.com' if env[:env] == :sandbox
|
90
|
-
api_endpoint = opts[:api_endpoint] if opts[:api_endpoint]
|
91
|
-
|
92
|
-
http_method = if opts[:http_method].nil?
|
93
|
-
:GET
|
94
|
-
else
|
95
|
-
opts[:http_method].to_s.upcase.scrub.strip.chomp.to_sym
|
96
|
-
end
|
97
|
-
api_call = opts[:api_call].to_s.scrub
|
98
|
-
params = opts[:params]
|
99
|
-
http_body = opts[:http_body].to_s.scrub.strip.chomp
|
100
|
-
|
101
|
-
max_conn_attempts = 30
|
102
|
-
conn_attempt = 0
|
103
|
-
|
104
|
-
begin
|
105
|
-
conn_attempt += 1
|
106
|
-
if option_choice.proxy
|
107
|
-
rest_client = RestClient
|
108
|
-
rest_client.proxy = option_choice.proxy
|
109
|
-
rest_client_request = rest_client::Request
|
110
|
-
else
|
111
|
-
rest_client_request = RestClient::Request
|
112
|
-
end
|
113
|
-
|
114
|
-
api_signature_response = generate_signature(
|
115
|
-
api_secret: api_secret,
|
116
|
-
http_method: http_method,
|
117
|
-
api_call: api_call,
|
118
|
-
params: params,
|
119
|
-
http_body: http_body
|
120
|
-
)
|
121
|
-
api_signature = api_signature_response[:api_signature]
|
122
|
-
api_timestamp = api_signature_response[:api_timestamp]
|
123
|
-
|
124
|
-
case http_method
|
125
|
-
when :GET
|
126
|
-
headers = {
|
127
|
-
content_type: 'application/json; charset=UTF-8',
|
128
|
-
CB_ACCESS_TIMESTAMP: api_timestamp,
|
129
|
-
CB_ACCESS_PASSPHRASE: api_passphrase,
|
130
|
-
CB_ACCESS_KEY: api_key,
|
131
|
-
CB_ACCESS_SIGN: api_signature
|
132
|
-
}
|
133
|
-
|
134
|
-
headers[:params] = params if params
|
135
|
-
headers[:ORDER_TYPE] = order_type if order_type
|
136
|
-
headers[:EVENT_NOTES] = event_notes if event_notes
|
137
|
-
|
138
|
-
response = rest_client_request.execute(
|
139
|
-
method: :GET,
|
140
|
-
url: "#{api_endpoint}#{api_call}",
|
141
|
-
headers: headers,
|
142
|
-
verify_ssl: false
|
143
|
-
)
|
144
|
-
|
145
|
-
when :DELETE
|
146
|
-
headers = {
|
147
|
-
content_type: 'application/json; charset=UTF-8',
|
148
|
-
CB_ACCESS_TIMESTAMP: api_timestamp,
|
149
|
-
CB_ACCESS_PASSPHRASE: api_passphrase,
|
150
|
-
CB_ACCESS_KEY: api_key,
|
151
|
-
CB_ACCESS_SIGN: api_signature
|
152
|
-
}
|
153
|
-
|
154
|
-
headers[:params] = params if params
|
155
|
-
headers[:ORDER_TYPE] = order_type if order_type
|
156
|
-
headers[:EVENT_NOTES] = event_notes if event_notes
|
157
|
-
|
158
|
-
response = rest_client_request.execute(
|
159
|
-
method: :DELETE,
|
160
|
-
url: "#{api_endpoint}#{api_call}",
|
161
|
-
headers: headers,
|
162
|
-
verify_ssl: false
|
163
|
-
)
|
164
|
-
|
165
|
-
when :POST
|
166
|
-
headers = {
|
167
|
-
content_type: 'application/json; charset=UTF-8',
|
168
|
-
CB_ACCESS_TIMESTAMP: api_timestamp,
|
169
|
-
CB_ACCESS_PASSPHRASE: api_passphrase,
|
170
|
-
CB_ACCESS_KEY: api_key,
|
171
|
-
CB_ACCESS_SIGN: api_signature
|
172
|
-
}
|
173
|
-
|
174
|
-
headers[:params] = params if params
|
175
|
-
headers[:ORDER_TYPE] = order_type if order_type
|
176
|
-
headers[:EVENT_NOTES] = event_notes if event_notes
|
177
|
-
|
178
|
-
response = rest_client_request.execute(
|
179
|
-
method: :POST,
|
180
|
-
url: "#{api_endpoint}#{api_call}",
|
181
|
-
headers: headers,
|
182
|
-
payload: http_body,
|
183
|
-
verify_ssl: false
|
184
|
-
)
|
185
|
-
|
186
|
-
else
|
187
|
-
raise @@logger.error("Unsupported HTTP Method #{http_method} for #{self} Plugin")
|
188
|
-
end
|
189
|
-
|
190
|
-
JSON.parse(response, symbolize_names: true)
|
191
|
-
rescue RestClient::Unauthorized => e
|
192
|
-
File.open('/tmp/cryptum-errors.txt', 'a') do |f|
|
193
|
-
f.puts Time.now.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
194
|
-
f.puts "#{self}\n#{e}\n\n\n"
|
195
|
-
end
|
196
|
-
|
197
|
-
raise e if conn_attempt > max_conn_attempts
|
198
|
-
|
199
|
-
sleep 60
|
200
|
-
retry
|
201
|
-
end
|
202
|
-
rescue RestClient::ExceptionWithResponse => e
|
203
|
-
File.open('/tmp/cryptum-errors.txt', 'a') do |f|
|
204
|
-
f.puts Time.now.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
205
|
-
f.puts "Module: #{self}"
|
206
|
-
f.puts "URL: #{api_endpoint}#{api_call}"
|
207
|
-
f.puts "PARAMS: #{params.inspect}"
|
208
|
-
f.puts "HTTP POST BODY: #{http_body.inspect}" if http_body != ''
|
209
|
-
f.puts "#{e}\n#{e.response}\n\n\n"
|
210
|
-
end
|
211
|
-
|
212
|
-
insufficient_funds = '{"message":"Insufficient funds"}'
|
213
|
-
size -= base_increment if e.response == insufficient_funds
|
214
|
-
|
215
|
-
sleep 0.3
|
216
|
-
retry
|
217
|
-
rescue RestClient::TooManyRequests => e
|
218
|
-
File.open('/tmp/cryptum-errors.txt', 'a') do |f|
|
219
|
-
f.puts Time.now.strftime('%Y-%m-%d %H:%M:%S.%N %z')
|
220
|
-
f.puts "Module: #{self}"
|
221
|
-
f.puts "URL: #{api_endpoint}#{api_call}"
|
222
|
-
f.puts "PARAMS: #{params.inspect}"
|
223
|
-
f.puts "HTTP POST BODY: #{http_body.inspect}" if http_body != ''
|
224
|
-
f.puts "#{e}\n#{e.response}\n\n\n"
|
225
|
-
end
|
226
|
-
|
227
|
-
sleep 1
|
228
|
-
retry
|
229
|
-
end
|
230
|
-
|
231
|
-
public_class_method def self.submit_limit_order(opts = {})
|
232
|
-
option_choice = opts[:option_choice]
|
233
|
-
env = opts[:env]
|
234
|
-
price = opts[:price]
|
235
|
-
size = opts[:size]
|
236
|
-
buy_or_sell = opts[:buy_or_sell]
|
237
|
-
event_history = opts[:event_history]
|
238
|
-
bot_conf = opts[:bot_conf]
|
239
|
-
buy_order_id = opts[:buy_order_id]
|
240
|
-
|
241
|
-
tpm = bot_conf[:target_profit_margin_percent].to_f
|
242
|
-
tpm_cast_as_decimal = tpm / 100
|
243
|
-
|
244
|
-
product_id = option_choice.symbol.to_s.gsub('_', '-').upcase
|
245
|
-
|
246
|
-
this_product = event_history.order_book[:this_product]
|
247
|
-
base_increment = this_product[:base_increment]
|
248
|
-
quote_increment = this_product[:quote_increment]
|
249
|
-
# crypto_smallest_decimal = base_increment.to_s.split('.')[-1].length
|
250
|
-
fiat_smallest_decimal = quote_increment.to_s.split('.')[-1].length
|
251
|
-
|
252
|
-
order_hash = {}
|
253
|
-
|
254
|
-
order_hash[:type] = 'limit'
|
255
|
-
order_hash[:time_in_force] = 'GTC'
|
256
|
-
|
257
|
-
if buy_or_sell == :buy
|
258
|
-
order_hash[:time_in_force] = 'GTT'
|
259
|
-
order_hash[:cancel_after] = 'min'
|
260
|
-
end
|
261
|
-
|
262
|
-
order_hash[:size] = size
|
263
|
-
order_hash[:price] = price
|
264
|
-
order_hash[:side] = buy_or_sell
|
265
|
-
order_hash[:product_id] = product_id
|
266
|
-
|
267
|
-
http_body = order_hash.to_json
|
268
|
-
|
269
|
-
limit_order_resp = rest_api_call(
|
270
|
-
option_choice: option_choice,
|
271
|
-
env: env,
|
272
|
-
http_method: :POST,
|
273
|
-
api_call: '/orders',
|
274
|
-
http_body: http_body,
|
275
|
-
base_increment: base_increment
|
276
|
-
)
|
277
|
-
|
278
|
-
# Populate Order ID on the Buy
|
279
|
-
# to know what to do on the Sell
|
280
|
-
case buy_or_sell
|
281
|
-
when :buy
|
282
|
-
this_order = event_history.order_book[:order_plan].shift
|
283
|
-
this_order[:buy_order_id] = limit_order_resp[:id]
|
284
|
-
this_order[:price] = price
|
285
|
-
targ_price = price.to_f + (price.to_f * tpm_cast_as_decimal)
|
286
|
-
this_order[:tpm] = format(
|
287
|
-
'%0.2f',
|
288
|
-
tpm
|
289
|
-
)
|
290
|
-
this_order[:target_price] = format(
|
291
|
-
"%0.#{fiat_smallest_decimal}f",
|
292
|
-
targ_price
|
293
|
-
)
|
294
|
-
|
295
|
-
this_order[:size] = size
|
296
|
-
|
297
|
-
this_order[:color] = :cyan
|
298
|
-
event_history.order_book[:order_history_meta].push(this_order)
|
299
|
-
when :sell
|
300
|
-
sell_order_id = limit_order_resp[:id]
|
301
|
-
event_history.order_book[:order_history_meta].each do |meta|
|
302
|
-
if meta[:buy_order_id] == buy_order_id
|
303
|
-
meta[:sell_order_id] = sell_order_id
|
304
|
-
meta[:color] = :yellow
|
305
|
-
end
|
306
|
-
end
|
307
|
-
end
|
308
|
-
|
309
|
-
event_history
|
310
|
-
rescue StandardError => e
|
311
|
-
raise e
|
312
|
-
end
|
313
|
-
|
314
|
-
public_class_method def self.gtfo(opts = {})
|
315
|
-
option_choice = opts[:option_choice]
|
316
|
-
env = opts[:env]
|
317
|
-
event_history = opts[:event_history]
|
318
|
-
|
319
|
-
# Cancel all open orders
|
320
|
-
cancel_all_open_orders(
|
321
|
-
env: env,
|
322
|
-
option_choice: option_choice
|
323
|
-
)
|
324
|
-
|
325
|
-
product_id = option_choice.symbol.to_s.gsub('_', '-').upcase
|
326
|
-
|
327
|
-
this_product = event_history.order_book[:this_product]
|
328
|
-
base_increment = this_product[:base_increment]
|
329
|
-
quote_increment = this_product[:quote_increment]
|
330
|
-
crypto_smallest_decimal = base_increment.to_s.split('.')[-1].length
|
331
|
-
fiat_smallest_decimal = quote_increment.to_s.split('.')[-1].length
|
332
|
-
|
333
|
-
# TODO: Calculate / Price / Size
|
334
|
-
last_three_prices_arr = []
|
335
|
-
last_ticker_price = event_history.order_book[:ticker_price].to_f
|
336
|
-
second_to_last_ticker_price = event_history.order_book[:ticker_price_second_to_last].to_f
|
337
|
-
third_to_last_ticker_price = event_history.order_book[:ticker_price_third_to_last].to_f
|
338
|
-
last_three_prices_arr.push(last_ticker_price)
|
339
|
-
last_three_prices_arr.push(second_to_last_ticker_price)
|
340
|
-
last_three_prices_arr.push(third_to_last_ticker_price)
|
341
|
-
limit_price = last_three_prices_arr.sort[1]
|
342
|
-
price = format(
|
343
|
-
"%0.#{fiat_smallest_decimal}f",
|
344
|
-
limit_price
|
345
|
-
)
|
346
|
-
|
347
|
-
crypto_currency = option_choice.symbol.to_s.upcase.split('_').first.to_sym
|
348
|
-
portfolio = event_history.order_book[:portfolio]
|
349
|
-
this_account = portfolio.select do |account|
|
350
|
-
account[:currency] == crypto_currency.to_s
|
351
|
-
end
|
352
|
-
balance = format(
|
353
|
-
"%0.#{crypto_smallest_decimal}f",
|
354
|
-
this_account.first[:balance]
|
355
|
-
)
|
356
|
-
|
357
|
-
current_crypto_fiat_value = format(
|
358
|
-
'%0.2f',
|
359
|
-
balance.to_f * price.to_f
|
360
|
-
)
|
361
|
-
|
362
|
-
order_hash = {}
|
363
|
-
|
364
|
-
order_hash[:type] = 'limit'
|
365
|
-
order_hash[:time_in_force] = 'GTT'
|
366
|
-
order_hash[:cancel_after] = 'min'
|
367
|
-
|
368
|
-
order_hash[:size] = balance
|
369
|
-
order_hash[:price] = price
|
370
|
-
order_hash[:side] = :sell
|
371
|
-
order_hash[:product_id] = product_id
|
372
|
-
|
373
|
-
http_body = order_hash.to_json
|
374
|
-
|
375
|
-
limit_order_resp = rest_api_call(
|
376
|
-
option_choice: option_choice,
|
377
|
-
env: env,
|
378
|
-
http_method: :POST,
|
379
|
-
api_call: '/orders',
|
380
|
-
http_body: http_body,
|
381
|
-
base_increment: base_increment
|
382
|
-
)
|
383
|
-
|
384
|
-
# Populate Order ID on the Buy
|
385
|
-
# to know what to do on the Sell
|
386
|
-
this_order = {}
|
387
|
-
this_order[:plan_no] = '0.0'
|
388
|
-
this_order[:fiat_available] = '0.00'
|
389
|
-
this_order[:risk_alloc] = current_crypto_fiat_value
|
390
|
-
this_order[:allocation_decimal] = '1.0'
|
391
|
-
this_order[:allocation_percent] = '100.0'
|
392
|
-
this_order[:invest] = current_crypto_fiat_value
|
393
|
-
this_order[:return] = current_crypto_fiat_value
|
394
|
-
this_order[:profit] = '0.0'
|
395
|
-
this_order[:buy_order_id] = 'N/A'
|
396
|
-
this_order[:price] = price
|
397
|
-
this_order[:tpm] = '0.00'
|
398
|
-
this_order[:target_price] = current_crypto_fiat_value
|
399
|
-
this_order[:size] = balance
|
400
|
-
this_order[:color] = :magenta
|
401
|
-
this_order[:sell_order_id] = limit_order_resp[:id]
|
402
|
-
|
403
|
-
event_history.order_book[:order_history_meta].push(this_order)
|
404
|
-
|
405
|
-
event_history
|
406
|
-
rescue StandardError => e
|
407
|
-
raise e
|
408
|
-
end
|
409
|
-
|
410
|
-
# public_class_method def self.cancel_open_order(opts = {})
|
411
|
-
# env = opts[:env]
|
412
|
-
# option_choice = opts[:option_choice]
|
413
|
-
# order_id = opts[:order_id]
|
414
|
-
# order_type = opts[:order_type]
|
415
|
-
|
416
|
-
# product_id = option_choice.symbol.to_s.gsub('_', '-').upcase
|
417
|
-
|
418
|
-
# order_hash = {}
|
419
|
-
# order_hash[:product_id] = product_id
|
420
|
-
|
421
|
-
# params = order_hash
|
422
|
-
|
423
|
-
# rest_api_call(
|
424
|
-
# env: env,
|
425
|
-
# http_method: :DELETE,
|
426
|
-
# api_call: "/orders/#{order_id}",
|
427
|
-
# option_choice: option_choice,
|
428
|
-
# params: params,
|
429
|
-
# order_type: order_type
|
430
|
-
# )
|
431
|
-
# rescue StandardError => e
|
432
|
-
# raise e
|
433
|
-
# end
|
434
|
-
|
435
|
-
public_class_method def self.cancel_all_open_orders(opts = {})
|
436
|
-
env = opts[:env]
|
437
|
-
option_choice = opts[:option_choice]
|
438
|
-
event_notes = opts[:event_notes]
|
439
|
-
|
440
|
-
product_id = option_choice.symbol.to_s.gsub('_', '-').upcase
|
441
|
-
|
442
|
-
order_hash = {}
|
443
|
-
order_hash[:product_id] = product_id
|
444
|
-
|
445
|
-
# http_body = order_hash.to_json
|
446
|
-
params = order_hash
|
447
|
-
|
448
|
-
canceled_order_id_arr = []
|
449
|
-
loop do
|
450
|
-
canceled_order_id_arr = rest_api_call(
|
451
|
-
env: env,
|
452
|
-
http_method: :DELETE,
|
453
|
-
api_call: '/orders',
|
454
|
-
option_choice: option_choice,
|
455
|
-
params: params,
|
456
|
-
event_notes: event_notes
|
457
|
-
)
|
458
|
-
|
459
|
-
break if canceled_order_id_arr.empty?
|
460
|
-
end
|
461
|
-
canceled_order_id_arr
|
462
|
-
rescue StandardError => e
|
463
|
-
raise e
|
464
|
-
end
|
465
|
-
|
466
|
-
public_class_method def self.get_products(opts = {})
|
467
|
-
option_choice = opts[:option_choice]
|
468
|
-
env = opts[:env]
|
469
|
-
|
470
|
-
products = rest_api_call(
|
471
|
-
option_choice: option_choice,
|
472
|
-
env: env,
|
473
|
-
http_method: :GET,
|
474
|
-
api_call: '/products'
|
475
|
-
)
|
476
|
-
|
477
|
-
if products.length.positive?
|
478
|
-
supported_products_filter = products.select do |product|
|
479
|
-
product[:id].match?(/USD$/) &&
|
480
|
-
product[:status] == 'online' &&
|
481
|
-
product[:fx_stablecoin] == false
|
482
|
-
end
|
483
|
-
sorted_products = supported_products_filter.sort_by { |hash| hash[:id] }
|
484
|
-
end
|
485
|
-
|
486
|
-
sorted_products
|
487
|
-
rescue StandardError => e
|
488
|
-
raise e
|
489
|
-
end
|
490
|
-
|
491
|
-
# List Supported Cryptum Products and Exit
|
492
|
-
public_class_method def self.list_products_and_exit(opts = {})
|
493
|
-
option_choice = opts[:option_choice]
|
494
|
-
env = opts[:env]
|
495
|
-
|
496
|
-
puts "\n#{option_choice.driver_name} Supports the Following Products:"
|
497
|
-
products = Cryptum::API.get_products(
|
498
|
-
option_choice: option_choice,
|
499
|
-
env: env
|
500
|
-
)
|
501
|
-
|
502
|
-
products.map do |product|
|
503
|
-
puts product[:id].downcase
|
504
|
-
end
|
505
|
-
|
506
|
-
exit 0
|
507
|
-
rescue StandardError => e
|
508
|
-
raise e
|
509
|
-
end
|
510
|
-
|
511
|
-
private_class_method def self.get_exchange_rates(opts = {})
|
512
|
-
option_choice = opts[:option_choice]
|
513
|
-
env = opts[:env]
|
514
|
-
|
515
|
-
api_endpoint = 'https://api.coinbase.com/v2'
|
516
|
-
exchange_rates_api_call = '/exchange-rates'
|
517
|
-
|
518
|
-
# We don't always get fees back from Coinbase...
|
519
|
-
# This is a hack to ensure we do.
|
520
|
-
exchange = {}
|
521
|
-
exchange = rest_api_call(
|
522
|
-
option_choice: option_choice,
|
523
|
-
env: env,
|
524
|
-
http_method: :GET,
|
525
|
-
api_endpoint: api_endpoint,
|
526
|
-
api_call: exchange_rates_api_call
|
527
|
-
)
|
528
|
-
|
529
|
-
exchange[:data][:rates]
|
530
|
-
rescue StandardError => e
|
531
|
-
raise e
|
532
|
-
end
|
533
|
-
|
534
|
-
public_class_method def self.get_portfolio(opts = {})
|
535
|
-
option_choice = opts[:option_choice]
|
536
|
-
env = opts[:env]
|
537
|
-
crypto = opts[:crypto]
|
538
|
-
fiat = opts[:fiat]
|
539
|
-
fiat_portfolio_file = opts[:fiat_portfolio_file]
|
540
|
-
event_notes = opts[:event_notes]
|
541
|
-
|
542
|
-
# Retrieve Exchange Rates
|
543
|
-
exchange_rates = get_exchange_rates(
|
544
|
-
option_choice: option_choice,
|
545
|
-
env: env
|
546
|
-
)
|
547
|
-
|
548
|
-
portfolio_complete_arr = []
|
549
|
-
portfolio_complete_arr = rest_api_call(
|
550
|
-
option_choice: option_choice,
|
551
|
-
env: env,
|
552
|
-
http_method: :GET,
|
553
|
-
api_call: '/accounts',
|
554
|
-
event_notes: event_notes
|
555
|
-
)
|
556
|
-
|
557
|
-
all_products = portfolio_complete_arr.select do |products|
|
558
|
-
products if products[:balance].to_f.positive?
|
559
|
-
end
|
560
|
-
|
561
|
-
total_holdings = 0.00
|
562
|
-
all_products.each do |product|
|
563
|
-
currency = product[:currency].to_sym
|
564
|
-
this_exchange_rate = exchange_rates[currency].to_f
|
565
|
-
total_holdings += product[:balance].to_f / this_exchange_rate
|
566
|
-
end
|
567
|
-
|
568
|
-
crypto_portfolio = portfolio_complete_arr.select do |product|
|
569
|
-
product if product[:currency] == crypto
|
570
|
-
end
|
571
|
-
|
572
|
-
fiat_portfolio = portfolio_complete_arr.select do |product|
|
573
|
-
product if product[:currency] == fiat
|
574
|
-
end
|
575
|
-
fiat_portfolio.last[:total_holdings] = format(
|
576
|
-
'%0.8f',
|
577
|
-
total_holdings
|
578
|
-
)
|
579
|
-
|
580
|
-
File.write(
|
581
|
-
fiat_portfolio_file,
|
582
|
-
JSON.pretty_generate(fiat_portfolio)
|
583
|
-
)
|
584
|
-
|
585
|
-
crypto_portfolio
|
586
|
-
rescue StandardError => e
|
587
|
-
raise e
|
588
|
-
end
|
589
|
-
|
590
|
-
public_class_method def self.get_fees(opts = {})
|
591
|
-
option_choice = opts[:option_choice]
|
592
|
-
env = opts[:env]
|
593
|
-
|
594
|
-
fees_api_call = '/fees'
|
595
|
-
|
596
|
-
# We don't always get fees back from Coinbase...
|
597
|
-
# This is a hack to ensure we do.
|
598
|
-
fees = {}
|
599
|
-
fees = rest_api_call(
|
600
|
-
option_choice: option_choice,
|
601
|
-
env: env,
|
602
|
-
http_method: :GET,
|
603
|
-
api_call: fees_api_call
|
604
|
-
)
|
605
|
-
|
606
|
-
fees
|
607
|
-
rescue StandardError => e
|
608
|
-
raise e
|
609
|
-
end
|
12
|
+
require 'cryptum/api/exchange_rates'
|
13
|
+
require 'cryptum/api/fees'
|
14
|
+
require 'cryptum/api/order_history'
|
15
|
+
require 'cryptum/api/orders'
|
16
|
+
require 'cryptum/api/portfolio'
|
17
|
+
require 'cryptum/api/products'
|
18
|
+
require 'cryptum/api/rest'
|
19
|
+
require 'cryptum/api/signature'
|
610
20
|
|
611
21
|
# public_class_method def self.get_profiles(opts = {})
|
612
22
|
# option_choice = opts[:option_choice]
|
@@ -618,7 +28,7 @@ module Cryptum
|
|
618
28
|
# # This is a hack to ensure we do.
|
619
29
|
# profiles = {}
|
620
30
|
# # loop do
|
621
|
-
# profiles =
|
31
|
+
# profiles = Cryptum::API::Rest.call(
|
622
32
|
# option_choice: option_choice,
|
623
33
|
# env: env,
|
624
34
|
# http_method: :GET,
|
@@ -635,73 +45,13 @@ module Cryptum
|
|
635
45
|
# raise e
|
636
46
|
# end
|
637
47
|
|
638
|
-
public_class_method def self.get_order_history(opts = {})
|
639
|
-
option_choice = opts[:option_choice]
|
640
|
-
env = opts[:env]
|
641
|
-
|
642
|
-
product_id = option_choice.symbol.to_s.gsub('_', '-').upcase
|
643
|
-
|
644
|
-
orders_api_call = '/orders'
|
645
|
-
params = {}
|
646
|
-
params[:product_id] = product_id
|
647
|
-
params[:status] = 'all'
|
648
|
-
|
649
|
-
# We don't always get order_history back from Coinbase...
|
650
|
-
# This is a hack to ensure we do.
|
651
|
-
order_history = []
|
652
|
-
order_history = rest_api_call(
|
653
|
-
option_choice: option_choice,
|
654
|
-
env: env,
|
655
|
-
http_method: :GET,
|
656
|
-
api_call: orders_api_call,
|
657
|
-
params: params
|
658
|
-
)
|
659
|
-
|
660
|
-
# Cast UTC Timestamps as local times
|
661
|
-
order_history.each do |order|
|
662
|
-
order[:created_at] = Time.parse(
|
663
|
-
order[:created_at]
|
664
|
-
).localtime.to_s
|
665
|
-
|
666
|
-
next unless order[:done_at]
|
667
|
-
|
668
|
-
order[:done_at] = Time.parse(
|
669
|
-
order[:done_at]
|
670
|
-
).localtime.to_s
|
671
|
-
end
|
672
|
-
|
673
|
-
order_history
|
674
|
-
rescue StandardError => e
|
675
|
-
raise e
|
676
|
-
end
|
677
|
-
|
678
48
|
# Display Usage for this Module
|
679
49
|
|
680
50
|
public_class_method def self.help
|
681
51
|
puts "USAGE:
|
682
|
-
signature = #{self}.generate_signature(
|
683
|
-
api_secret: 'required - Coinbase Pro API Secret',
|
684
|
-
http_method: 'optional - Defaults to :GET',
|
685
|
-
api_call: 'optional - Defaults to /users/self/verify',
|
686
|
-
params: 'optional - HTTP GET Parameters',
|
687
|
-
http_body: 'optional HTTP POST Body'
|
688
|
-
)
|
689
|
-
|
690
52
|
profiles = #{self}.get_profiles(
|
691
53
|
env: 'required - Coinbase::Option::Environment.get Object'
|
692
54
|
)
|
693
|
-
|
694
|
-
products = #{self}.get_products(
|
695
|
-
env: 'required - Coinbase::Option::Environment.get Object'
|
696
|
-
)
|
697
|
-
|
698
|
-
portfolio = #{self}.get_portfolio(
|
699
|
-
env: 'required - Coinbase::Option::Environment.get Object'
|
700
|
-
)
|
701
|
-
|
702
|
-
order_history = #{self}.get_order_history(
|
703
|
-
env: 'required - Coinbase::Option::Environment.get Object'
|
704
|
-
)
|
705
55
|
"
|
706
56
|
end
|
707
57
|
end
|
data/lib/cryptum/event/buy.rb
CHANGED
@@ -117,7 +117,7 @@ module Cryptum
|
|
117
117
|
event_history.order_submitted = true
|
118
118
|
event_history.event_notes = "{ \"event_type\": \"#{event_history.event_type}\", \"cancel\": \"#{event_history.order_canceled}\", \"submitted\": \"#{event_history.order_submitted}\" }" if option_choice.proxy
|
119
119
|
|
120
|
-
event_history = Cryptum::API.submit_limit_order(
|
120
|
+
event_history = Cryptum::API::Orders.submit_limit_order(
|
121
121
|
option_choice: option_choice,
|
122
122
|
env: env,
|
123
123
|
price: price,
|
data/lib/cryptum/event/cancel.rb
CHANGED