cosgrove 0.0.1rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,100 @@
1
+ module Cosgrove
2
+ module Config
3
+ def cosgrove_secure
4
+ yml[:cosgrove][:secure]
5
+ end
6
+
7
+ def cosgrove_token
8
+ yml[:cosgrove][:token]
9
+ end
10
+
11
+ def cosgrove_client_id
12
+ yml[:cosgrove][:client_id]
13
+ end
14
+
15
+ def cosgrove_upvote_weight
16
+ yml[:cosgrove][:upvote_weight]
17
+ end
18
+
19
+ def steem_api_url
20
+ chain[:steem_api_url]
21
+ end
22
+
23
+ def golos_api_url
24
+ chain[:golos_api_url]
25
+ end
26
+
27
+ def test_api_url
28
+ chain[:test_api_url]
29
+ end
30
+
31
+ def steem_account
32
+ chain[:steem_account]
33
+ end
34
+
35
+ def steem_posting_wif
36
+ chain[:steem_posting_wif]
37
+ end
38
+
39
+ def golos_account
40
+ chain[:golos_account]
41
+ end
42
+
43
+ def golos_posting_wif
44
+ chain[:golos_posting_wif]
45
+ end
46
+
47
+ def test_posting_wif
48
+ chain[:test_posting_wif]
49
+ end
50
+
51
+ def discord_channels
52
+ return ENV['CHANNELS'].to_s.split(' ') if !!ENV['CHANNELS']
53
+
54
+ yml[:discord][:channels]
55
+ end
56
+
57
+ def discord_log_mode
58
+ return :debug if !!ENV['HELL_ENABLED']
59
+ return :info unless !!yml[:discord][:log_mode]
60
+
61
+ yml[:discord][:log_mode].to_sym
62
+ end
63
+
64
+ def channel_upvote_weight(channel_id)
65
+ rules = yml[:cosgrove][:upvote_rules][:channels]
66
+ default_weight = rules[:default][:upvote_weight] rescue '100.00 %'
67
+
68
+ keys = rules.keys - [:default]
69
+
70
+ weight = keys.map do |key|
71
+ rule = rules[key]
72
+ rule[:upvote_weight] if rule[:channel_id] == channel_id
73
+ end.compact.last
74
+
75
+ weight || default_weight
76
+ end
77
+ private
78
+ def chain
79
+ @chain ||= yml[:chain]
80
+ end
81
+
82
+ def yml
83
+ return @yml if !!@yml
84
+
85
+ config_yaml_path = "#{Cosgrove::PWD}/config.yml"
86
+
87
+ @yml = if File.exist?(config_yaml_path)
88
+ YAML.load_file(config_yaml_path)
89
+ else
90
+ raise "Create a file: #{config_yaml_path}"
91
+ end
92
+
93
+ if @yml[:cosgrove].nil? || @yml[:cosgrove][:secure].nil? || @yml[:cosgrove][:secure] == 'set this'
94
+ raise "Set secure key in #{config_yaml_path}."
95
+ end
96
+
97
+ @yml
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,411 @@
1
+ module Cosgrove
2
+ module Market
3
+ include Cosgrove::Support
4
+ include ActionView::Helpers::NumberHelper
5
+
6
+ def price_feed(chain = :steem)
7
+ feed_history = api(chain).get_feed_history.result
8
+ base_per_mvest = api(chain).steem_per_mvest
9
+
10
+ current_median_history = feed_history.current_median_history
11
+ base = current_median_history.base
12
+ base = base.split(' ').first.to_f
13
+ quote = current_median_history.quote
14
+ quote = quote.split(' ').first.to_f
15
+
16
+ base_per_debt = (base / quote) * base_per_mvest
17
+
18
+ [base_per_mvest, base_per_debt]
19
+ end
20
+
21
+ # https://api.vaultoro.com/#api-Basic_API-getMarkets
22
+ def gold_price
23
+ begin
24
+ gold_market = JSON[open("https://api.vaultoro.com/markets").read]
25
+ pol_usdt_btc = JSON[open("https://poloniex.com/public?command=returnOrderBook&currencyPair=USDT_BTC&depth=10").read]
26
+ pol_usdt_btc = pol_usdt_btc['asks'].first.first.to_f
27
+ btc_gld = gold_market['data']['LastPrice'].to_f
28
+
29
+ [btc_gld, (btc_gld * pol_usdt_btc) / 1000] # btc_gld, usdt_gld_per_milli
30
+ rescue
31
+ [0, 0]
32
+ end
33
+ end
34
+
35
+ def market_data
36
+ pol_btc_steem = JSON[open("https://poloniex.com/public?command=returnOrderBook&currencyPair=BTC_STEEM&depth=10").read]
37
+ pol_btc_sbd = JSON[open("https://poloniex.com/public?command=returnOrderBook&currencyPair=BTC_SBD&depth=10").read]
38
+ pol_usdt_btc = JSON[open("https://poloniex.com/public?command=returnOrderBook&currencyPair=USDT_BTC&depth=10").read]
39
+
40
+ pol_btc_steem = pol_btc_steem['asks'].first.first.to_f
41
+ pol_btc_sbd = pol_btc_sbd['asks'].first.first.to_f
42
+ pol_usdt_btc = pol_usdt_btc['asks'].first.first.to_f
43
+
44
+ pol_usdt_sbd = pol_usdt_btc * pol_btc_sbd
45
+ pol_usdt_steem = pol_usdt_btc * pol_btc_steem
46
+ # pol_sbd_steem = pol_usdt_sbd * ( pol_usdt_btc * pol_btc_steem )
47
+
48
+ btx_btc_golos = JSON[open("https://bittrex.com/api/v1.1/public/getmarketsummary?market=btc-golos").read]
49
+ btx_btc_gbg = JSON[open("https://bittrex.com/api/v1.1/public/getmarketsummary?market=btc-gbg").read]
50
+ btx_usdt_btc = JSON[open("https://bittrex.com/api/v1.1/public/getmarketsummary?market=usdt-btc").read]
51
+
52
+ btx_btc_golos = btx_btc_golos['result'].first['Ask'].to_f
53
+ btx_btc_gbg = btx_btc_gbg['result'].first['Ask'].to_f
54
+ btx_usdt_btc = btx_usdt_btc['result'].first['Ask'].to_f
55
+
56
+ btx_usdt_gbg = btx_usdt_btc * btx_btc_gbg
57
+ btx_usdt_golos = btx_usdt_btc * btx_btc_golos
58
+ # btx_gbg_golos = btx_usdt_gbg * ( btx_usdt_btc * btx_btc_golos )
59
+
60
+ [pol_usdt_steem, pol_usdt_sbd, btx_usdt_golos, btx_usdt_gbg]
61
+ end
62
+
63
+ def mvests(chain = :steem, account_names = [])
64
+ pol_usdt_steem, _pol_usdt_sbd, btx_usdt_golos, _btx_usdt_gbg = market_data
65
+ base_per_mvest, base_per_debt = price_feed(chain)
66
+
67
+ if account_names.none?
68
+ pol_base_per_dept_as_usdt = base_per_mvest * pol_usdt_steem
69
+ pol_base_per_dept_as_usdt = number_to_currency(pol_base_per_dept_as_usdt, precision: 3)
70
+
71
+ base_per_mvest = number_with_precision(base_per_mvest, precision: 3, delimiter: ',', separator: '.')
72
+
73
+ base_per_debt = if chain == :steem
74
+ number_to_currency(base_per_debt, precision: 3)
75
+ else
76
+ _btc_gld, usdt_gld_per_milli = gold_price
77
+ base_per_debt_as_usdt = base_per_debt * usdt_gld_per_milli
78
+ base_per_debt_as_usdt = number_to_currency(base_per_debt_as_usdt, precision: 3)
79
+
80
+ number_with_precision(base_per_debt, precision: 3, delimiter: ',', separator: '.')
81
+ end
82
+
83
+ # E.g. from 2016/11/25: 1 MV = 1M VESTS = 459.680 STEEM = $50.147
84
+ return case chain
85
+ when :steem then "`1 MV = 1M VESTS = #{base_per_mvest} STEEM = #{base_per_debt} = #{pol_base_per_dept_as_usdt} on Poloniex`"
86
+ when :golos then "`1 MG = 1M GESTS = #{base_per_mvest} GOLOS = #{base_per_debt} GBG = #{base_per_debt_as_usdt}`"
87
+ when :test then "`1 MT = 1M TESTS = #{base_per_mvest} TEST = #{base_per_debt}`"
88
+ end
89
+ end
90
+
91
+ # Instead of all MVESTS for the entire chain, we are looking at specific
92
+ # account names or a search pattern thereof.
93
+
94
+ case chain
95
+ when :steem
96
+ vests = 0
97
+ delegated_vests = 0
98
+ received_vests = 0
99
+ account = nil
100
+ wildcards = false
101
+
102
+ account_names.each do |a|
103
+ if a =~ /.*\*$/
104
+ wildcards = true
105
+ lower_bound_name = a.split('*').first
106
+ response = api(chain).lookup_accounts(a, 1000)
107
+ result = response.result
108
+
109
+ if result.any?
110
+ account_names -= [a]
111
+ account_names += [lower_bound_name]
112
+ account_names += result.map { |name| name if name =~ /#{lower_bound_name}.*/ }.compact
113
+ end
114
+ end
115
+ end
116
+
117
+ account_names = account_names.map(&:downcase).uniq
118
+ accounts = SteemData::Account.where(:name.in => account_names)
119
+ account = accounts.last
120
+
121
+ if accounts.count == account_names.size
122
+ vests = accounts.sum('vesting_shares.amount')
123
+ delegated_vests = accounts.sum('delegated_vesting_shares.amount')
124
+ received_vests = accounts.sum('received_vesting_shares.amount')
125
+ elsif !wildcards
126
+ valid_names = accounts.distinct(:name)
127
+ unknown_names = account_names - valid_names
128
+ return unknown_account(unknown_names.first)
129
+ end
130
+
131
+ if accounts.count == 1 && vests == 0.0
132
+ # Falling back to RPC because balance is out of date and we only want
133
+ # a single account.
134
+ response = api(:steem).get_accounts([account.name])
135
+ account = response.result.first
136
+ vests = account.vesting_shares.split(' ').first.to_f
137
+ delegated_vests = account.delegated_vesting_shares.split(' ').first.to_f
138
+ received_vests = account.received_vesting_shares.split(' ').first.to_f
139
+ end
140
+
141
+ mvests = vests / 1000000
142
+ delegated_vests = (received_vests - delegated_vests) / 1000000
143
+ steem = base_per_mvest * mvests
144
+ sbd = base_per_debt * mvests
145
+ pol_sbd = base_per_mvest * mvests * pol_usdt_steem
146
+
147
+ mvests = number_with_precision(mvests, precision: 3, delimiter: ',', separator: '.')
148
+ delegated_sign = delegated_vests >= 0.0 ? '+' : '-'
149
+ delegated_vests = number_with_precision(delegated_vests.abs, precision: 3, delimiter: ',', separator: '.')
150
+ steem = number_with_precision(steem, precision: 3, delimiter: ',', separator: '.')
151
+ sbd = number_to_currency(sbd, precision: 3)
152
+ pol_sbd = number_to_currency(pol_sbd, precision: 3)
153
+
154
+ if accounts.size == 1
155
+ balance = ["#{mvests} MVESTS = #{steem} STEEM = #{sbd} = #{pol_sbd} on Poloniex"]
156
+ unless delegated_vests == '0.000'
157
+ balance << "(#{delegated_sign}#{delegated_vests} MVESTS delegated)"
158
+ end
159
+
160
+ "**#{account.name}:** `#{balance.join(' ')}`"
161
+ else
162
+ balance = ["#{mvests} MVESTS = #{steem} STEEM = #{sbd} = #{pol_sbd} on Poloniex"]
163
+ unless delegated_vests == '0.000'
164
+ balance << "(#{delegated_sign}#{delegated_vests} MVESTS delegated)"
165
+ end
166
+
167
+ "**#{pluralize(accounts.count, 'account')}:** `#{balance.join(' ')}`"
168
+ end
169
+ when :golos
170
+ response = api(:golos).get_accounts(account_names)
171
+ account = response.result.first
172
+ gests = account.vesting_shares.split(' ').first.to_f
173
+
174
+ mgests = gests / 1000000
175
+ golos = base_per_mvest * mgests
176
+ gbg = base_per_debt * mgests
177
+ _btc_gld, usdt_gld_per_milli = gold_price
178
+ usd = gbg * usdt_gld_per_milli
179
+
180
+ mgests = number_with_precision(mgests, precision: 3, delimiter: ',', separator: '.')
181
+ golos = number_with_precision(golos, precision: 3, delimiter: ',', separator: '.')
182
+ gbg = number_with_precision(gbg, precision: 3, delimiter: ',', separator: '.')
183
+ usd = number_to_currency(usd, precision: 3)
184
+
185
+ "**#{account.name}:** `#{mgests} MGESTS = #{golos} GOLOS = #{gbg} GBG = #{usd}`"
186
+ when :test then "Query not supported. No Mongo for Testnet."
187
+ end
188
+ end
189
+
190
+ def rewardpool(chain = :steem)
191
+ pol_usdt_steem, _pol_usdt_sbd, btx_usdt_golos, _btx_usdt_gbg = market_data
192
+ base_per_mvest, base_per_debt = price_feed(chain)
193
+
194
+ case chain
195
+ when :steem
196
+ response = api(chain).get_reward_fund 'post'
197
+ reward_fund = response.result
198
+ total = reward_fund.reward_balance
199
+ total = total.split(' ').first.to_f
200
+ total_usd = (total / base_per_mvest) * base_per_debt
201
+ pol_total_usd = total * pol_usdt_steem
202
+
203
+ total = number_with_precision(total, precision: 0, delimiter: ',', separator: '.')
204
+ total_usd = number_to_currency(total_usd, precision: 0)
205
+ pol_total_usd = number_to_currency(pol_total_usd, precision: 0)
206
+
207
+ "Total Reward Fund: `#{total} STEEM (Worth: #{total_usd} internally; #{pol_total_usd} on Poloniex)`"
208
+ when :golos
209
+ response = api(chain).get_dynamic_global_properties
210
+ properties = response.result
211
+ total = properties.total_reward_fund_steem
212
+ total = total.split(' ').first.to_f
213
+ total_gbg = (total / base_per_mvest) * base_per_debt
214
+ btx_total_usd = total * btx_usdt_golos
215
+
216
+ total = number_with_precision(total, precision: 0, delimiter: ',', separator: '.')
217
+ total_gbg = number_with_precision(total_gbg, precision: 0, delimiter: ',', separator: '.')
218
+ btx_total_usd = number_to_currency(btx_total_usd, precision: 0)
219
+
220
+ "Total Reward Fund: `#{total} GOLOS (Worth: #{total_gbg} GBG internally; #{btx_total_usd} on Bittrex)`"
221
+ when :test
222
+ "Total Reward Fund: `#{('%.3f' % total)} TEST (Worth: #{('%.3f' % total_usd)} TBD internally)`"
223
+ end
224
+ end
225
+
226
+ def ticker
227
+ pol_usdt_steem, pol_usdt_sbd, btx_usdt_golos, btx_usdt_gbg = market_data
228
+
229
+ pol_usdt_steem = number_to_currency(pol_usdt_steem, precision: 4)
230
+ pol_usdt_sbd = number_to_currency(pol_usdt_sbd, precision: 4)
231
+ btx_usdt_golos = number_to_currency(btx_usdt_golos, precision: 4)
232
+ btx_usdt_gbg = number_to_currency(btx_usdt_gbg, precision: 4)
233
+
234
+ ticker = []
235
+ ticker << "`Poloniex: USD/STEEM: #{pol_usdt_steem}; USD/SBD: #{pol_usdt_sbd}`"
236
+ ticker << "`Bittrex: USD/GOLOS: #{btx_usdt_golos}; USD/GBG: #{btx_usdt_gbg}`"
237
+ ticker.join("\n")
238
+ end
239
+
240
+ def promoted(chain = :steem, period = :today)
241
+ return "Query not supported. No Mongo for #{chain.to_s.capitalize}." unless chain == :steem
242
+
243
+ promoted = SteemData::AccountOperation.type('transfer').where(account: 'null', to: 'null', 'amount.asset' => 'SBD').send(period)
244
+ count_promoted = promoted.count
245
+ sum_promoted = promoted.sum('amount.amount')
246
+
247
+ base_per_mvest, base_per_debt = price_feed(chain)
248
+ response = api(chain).get_reward_fund 'post'
249
+ reward_fund = response.result
250
+ total = reward_fund.reward_balance
251
+ total = total.split(' ').first.to_f
252
+
253
+ total_usd = (total / base_per_mvest) * base_per_debt
254
+ ratio = (sum_promoted / total_usd) * 100
255
+
256
+ sum_promoted = number_to_currency(sum_promoted, precision: 3)
257
+ ratio = number_to_percentage(ratio, precision: 3)
258
+
259
+ "#{pluralize(count_promoted, 'post')} promoted #{period} totalling #{sum_promoted} (#{ratio} the size of reward pool)."
260
+ end
261
+
262
+ def supply(chain)
263
+ base_per_mvest, base_per_debt = price_feed(chain)
264
+
265
+ response = api(chain).get_dynamic_global_properties
266
+ properties = response.result
267
+ current_supply = properties.current_supply.split(' ').first.to_f
268
+ current_debt_supply = properties.current_sbd_supply.split(' ').first.to_f
269
+ total_vesting_fund_steem = properties.total_vesting_fund_steem.split(' ').first.to_f
270
+
271
+ total_base = (current_supply / base_per_mvest) * base_per_debt
272
+ ratio = (current_debt_supply / total_base) * 100
273
+
274
+ supply = []
275
+ case chain
276
+ when :steem
277
+ response = api(chain).get_reward_fund 'post'
278
+ reward_fund = response.result
279
+ reward_balance = reward_fund.reward_balance
280
+ reward_balance = reward_balance.split(' ').first.to_f
281
+
282
+ liquid_supply = current_supply - reward_balance - total_vesting_fund_steem
283
+ ratio_liquid = (liquid_supply / current_supply) * 100
284
+
285
+ current_supply = number_with_precision(current_supply, precision: 0, delimiter: ',', separator: '.')
286
+ ratio = number_to_percentage(ratio, precision: 3)
287
+ total_base = number_to_currency(total_base, precision: 0)
288
+ current_debt_supply = number_to_currency(current_debt_supply, precision: 0)
289
+ liquid_supply = number_with_precision(liquid_supply, precision: 0, delimiter: ',', separator: '.')
290
+ ratio_liquid = number_to_percentage(ratio_liquid, precision: 3)
291
+
292
+ supply << ["#{current_supply} STEEM (Worth #{total_base} SBD)"]
293
+ supply << ["#{current_debt_supply} SBD (#{ratio} of supply)"]
294
+ supply << ["#{liquid_supply} Liquid STEEM (#{ratio_liquid} of supply)"]
295
+ when :golos
296
+ properties = response.result
297
+ reward_balance = properties.total_reward_fund_steem
298
+ reward_balance = reward_balance.split(' ').first.to_f
299
+
300
+ liquid_supply = current_supply - reward_balance - total_vesting_fund_steem
301
+ ratio_liquid = (liquid_supply / current_supply) * 100
302
+
303
+ current_supply = number_with_precision(current_supply, precision: 0, delimiter: ',', separator: '.')
304
+ ratio = number_to_percentage(ratio, precision: 3)
305
+ total_base = number_with_precision(total_base, precision: 0, delimiter: ',', separator: '.')
306
+ current_debt_supply = number_with_precision(current_debt_supply, precision: 0, delimiter: ',', separator: '.')
307
+ liquid_supply = number_with_precision(liquid_supply, precision: 0, delimiter: ',', separator: '.')
308
+ ratio_liquid = number_to_percentage(ratio_liquid, precision: 3)
309
+
310
+ supply << ["#{current_supply} GOLOS (Worth #{total_base} GBG)"]
311
+ supply << ["#{current_debt_supply} GBG (#{ratio} of supply)"]
312
+ supply << ["#{liquid_supply} Liquid GOLOS (#{ratio_liquid} of supply)"]
313
+ when :test
314
+ properties = response.result
315
+ reward_balance = properties.total_reward_fund_steem
316
+ reward_balance = reward_balance.split(' ').first.to_f
317
+
318
+ liquid_supply = current_supply - reward_balance - total_vesting_fund_steem
319
+ ratio_liquid = (liquid_supply / current_supply) * 100
320
+
321
+ current_supply = number_with_precision(current_supply, precision: 0, delimiter: ',', separator: '.')
322
+ ratio = number_to_percentage(ratio, precision: 3)
323
+ total_base = number_with_precision(total_base, precision: 0, delimiter: ',', separator: '.')
324
+ current_debt_supply = number_with_precision(current_debt_supply, precision: 0, delimiter: ',', separator: '.')
325
+ liquid_supply = number_with_precision(liquid_supply, precision: 0, delimiter: ',', separator: '.')
326
+ ratio_liquid = number_to_percentage(ratio_liquid, precision: 3)
327
+
328
+ supply << ["#{current_supply} CORE (Worth #{total_base} TBD)"]
329
+ supply << ["#{current_debt_supply} TBD (#{ratio} of supply)"]
330
+ supply << ["#{liquid_supply} Liquid CORE (#{ratio_liquid} of supply)"]
331
+ end
332
+
333
+ "Supply: `" + supply.join('; ') + "`"
334
+ end
335
+
336
+ def mvests_sum(options = {})
337
+ chain = options[:chain] || :steem
338
+ account_names = options[:account_names]
339
+ case chain
340
+ when :steem then SteemData::Account.where(:name.in => account_names).sum('vesting_shares.amount')
341
+ when :golos then "Query not supported. No Mongo for Golos."
342
+ when :test then "Query not supported. No Mongo for Testnet."
343
+ end
344
+ end
345
+
346
+ def debt_exchange_rate(chain = :steem, limit = 19)
347
+ chain = chain.to_sym
348
+ response = api(chain).get_witnesses_by_vote('', limit)
349
+ witnesses = response.result
350
+ rates = witnesses.map(&:sbd_exchange_rate)
351
+
352
+ symbol = case chain
353
+ when :steem then 'SBD'
354
+ when :golos then 'GBG'
355
+ end
356
+
357
+ ratio = rates.map do |r|
358
+ b = r.base.split(' ').first.to_f
359
+ q = r.quote.split(' ').first.to_f
360
+
361
+ b / q unless q == 0.0
362
+ end.compact
363
+
364
+ price = ratio.sum / ratio.size
365
+ price = number_with_precision(price, precision: 3, delimiter: ',', separator: '.')
366
+
367
+ "#{price} #{symbol}"
368
+ end
369
+
370
+ def apr(chain = :steem, limit = 19)
371
+ chain = chain.to_sym
372
+ response = api(chain).get_witnesses_by_vote('', limit)
373
+ witnesses = response.result
374
+ rates = witnesses.map(&:props).map { |p| p['sbd_interest_rate'] }
375
+
376
+ rate = rates.sum / rates.size
377
+ rate = rate / 100.0
378
+ number_to_percentage(rate, precision: 3)
379
+ end
380
+
381
+ def effective_apr(chain = :steem)
382
+ chain = chain.to_sym
383
+ response = api(chain).get_dynamic_global_properties
384
+ properties = response.result
385
+ rate = properties.sbd_interest_rate
386
+
387
+ rate = rate / 100.0
388
+ number_to_percentage(rate, precision: 3)
389
+ end
390
+
391
+ def effective_price(chain = :steem)
392
+ chain = chain.to_sym
393
+ response = api(chain).get_feed_history
394
+ feed_history = response.result
395
+ current_median_history = feed_history.current_median_history
396
+
397
+ b = current_median_history.base.split(' ').first.to_f
398
+ q = current_median_history.quote.split(' ').first.to_f
399
+
400
+ symbol = case chain
401
+ when :steem then 'SBD'
402
+ when :golos then 'GBG'
403
+ end
404
+
405
+ price = b / q
406
+ price = number_with_precision(price, precision: 3, delimiter: ',', separator: '.')
407
+
408
+ "#{price} #{symbol}"
409
+ end
410
+ end
411
+ end