cosgrove 0.0.1rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,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