class MyCoins

Attributes

ccf[R]
mycurrency[RW]

Public Class Methods

new(source, date: nil, debug: false, mycurrency: 'USD', filepath: 'mycoins', colored: true, exchangerate_key: nil) click to toggle source
# File lib/mycoins.rb, line 15
def initialize(source, date: nil, debug: false, mycurrency: 'USD', 
               filepath: 'mycoins', colored: true, exchangerate_key: nil)

  @debug, @filepath, @colored = debug, filepath, colored
  @exchangerate_key = exchangerate_key
  
  @jer = JustExchangeRates.new(base: 'USD', debug: @debug, 
                               app_id: exchangerate_key)

  s = RXFHelper.read(source).first

  if s =~ /<\?dynarex / then

    @dx = Dynarex.new
    @dx.import s     

  end

  puts '@dx.to_xml: ' + @dx.to_xml if @debug

  @mycurrency = (@dx.currency || mycurrency).upcase
  puts '@mycurrency: ' + @mycurrency.inspect if @debug
  
  coin_names = @dx.all.map{ |x| x.title.gsub(/\s+\[[^\]]+\]/,'')}.uniq
  
  @cache_file = File.join(filepath, 'mycoins_lookup.yaml')
  
  h = if File.exist? @cache_file then

    puts 'reading coins symbols frome the cache' if @debug
    h2 = Psych.load(File.read(@cache_file))
    puts 'h2: ' + h2.inspect if @debug
    
    if (coin_names - h2.keys).empty? then
      h2
    else
      fetch_symbols coin_names 
    end
    
  else

    fetch_symbols coin_names
  end
  
  puts 'h: ' + h.inspect if @debug
  mycoins = h.values

  puts 'mycoins: ' + mycoins.inspect if @debug
  @ccf = CryptocoinFanboi.new(watch: mycoins, debug: debug, 
                              exchangerate_key: exchangerate_key)

end

Public Instance Methods

archive() click to toggle source
# File lib/mycoins.rb, line 68
def archive()

  filepath = File.join(@filepath, Time.now.year.to_s, 
                       Time.now.strftime("c%d%m%Y.xml"))
  FileUtils.mkdir_p File.dirname(filepath)
  File.write filepath, to_xml
  
end
portfolio(order_by: :rank) click to toggle source
# File lib/mycoins.rb, line 135
def portfolio(order_by: :rank)
  
  puts 'inside portfolio' if @debug
  r = build_portfolio(@dx.title)
  format_portfolio(r, order_by: order_by)

end
price(coin_name, qty, btc: nil, date: nil) click to toggle source

return the value of a coin given either the qty or purchase amount in BTC

# File lib/mycoins.rb, line 122
def price(coin_name, qty, btc: nil, date: nil)
  
  price_usd = if date then
    @ccf.price coin_name, date
  else
    coin = @ccf.find(coin_name)      
    coin.price_usd.to_f      
  end

  "%.2f %s" % [(price_usd * qty.to_f) * @jer.rate(@mycurrency), 
               @mycurrency]
end
to_dx() click to toggle source
# File lib/mycoins.rb, line 77
def to_dx()
  
  r = build_portfolio(@dx.title)
  dx = Dynarex.new    
  dx.import r.records    
  
  h = r.to_h
  h.delete :records
  dx.summary.merge!(h)
  
  dx
  
end
to_openstruct() click to toggle source
# File lib/mycoins.rb, line 91
def to_openstruct()
  build_portfolio(@dx.title)
end
to_percentages() click to toggle source
# File lib/mycoins.rb, line 95
def to_percentages()
  
  all = self.to_dx.to_a
  val = ('value_' + @mycurrency.downcase).to_sym
  
  a = all.map {|x| x[val].to_f }
  sum = a.inject(&:+)
  
  a2 = all.map do |x| 
    [x[:title].sub(/\s+\[[^\]]\]+/,''), (x[val].to_f).round(2)]
  end.group_by(&:first).to_a
  
  a3 = a2.map do |x| 
    [x.first, ((x[1].inject(0) {|r,y| r + y[1].to_f} / sum.to_f) * 100)\
     .round(2)]
  end
  
  a3.sort_by(&:last).reverse.to_h

end
to_s() click to toggle source
# File lib/mycoins.rb, line 116
def to_s()
  @ccf.to_s
end
to_values() click to toggle source

returns a Hash object containing the value as well as the percentage relative to the total value of the portfolio for each cryptocurrency

# File lib/mycoins.rb, line 146
def to_values()
  
  portfolio = build_portfolio()
  
  self.to_percentages.inject({}) do |r, x|
    currency, pct = x
    r.merge!(currency => [pct, ((portfolio.value * pct) / 100).round(2)])
  end
  
end
to_xml() click to toggle source
# File lib/mycoins.rb, line 157
def to_xml()
  
  self.to_dx().to_xml pretty: true
  
end

Private Instance Methods

build_portfolio(title='CryptoCurrency Portfolio ' + Time.now.year.to_s) click to toggle source
# File lib/mycoins.rb, line 165
def build_portfolio(title='CryptoCurrency Portfolio ' + Time.now.year.to_s)
  
  if @portfolio and \
      DateTime.parse(@portfolio.datetime) + 60 > DateTime.now then
    return @portfolio      
  end
  
  a = build_records()
  
  invested = sum(a, :paid)
  gross_profit_list, losses_list = a.partition {|x| x[:roi].to_f > 0}
  gross_profit, losses = [gross_profit_list, losses_list]\
      .map{|x| sum(x, :roi)}
  pct_gross_profit = (100 / (invested / gross_profit)).round(2)
  pct_losses = (100 / (invested / losses)).round(2)
  total_value = sum(a, ('value_' + @mycurrency.downcase).to_sym)
  rate = JustExchangeRates.new(base: 'GBP', app_id: @exchangerate_key).rate('USD')

  btc = @ccf.price('bitcoin')
  
  if @debug then
    puts 'rate: ' + rate.inspect
    puts 'bicoin price: ' + btc.inspect
    puts 'total value: ' + total_value.inspect
  end    

  btc_val = ((total_value * rate) / btc).round(4)
  

  h = {
    title: title,
    mycurrency: @mycurrency,
    records: a,
    datetime: Time.now.strftime("%d/%m/%Y at %H:%M%p"),
    btc_price: btc,
    invested: invested,
    value: total_value, btc_value: btc_val,
    gross_profit: gross_profit.round(2), losses: losses.round(2),
    pct_gross_profit: pct_gross_profit,
    pct_losses: pct_losses,
    net_profit: sum(a, :roi).round(2),
    pct_net_profit: (pct_gross_profit + pct_losses).round(2)
  }
  
  @portfolio = OpenStruct.new(h)
  
end
build_records() click to toggle source
# File lib/mycoins.rb, line 213
def build_records()
  
  # print out the coin name, qty,value in USD,value in Sterling, and BTC
  @dx.all.inject([]) do |r, x|

    puts 'x: ' + x.inspect if @debug
    title = x.title.gsub(/\s+\[[^\]]+\]/,'')
    
    if @debug then
      puts 'title: '  + title.inspect
      puts '@ccf: ' + @ccf.class.inspect
    end
    
    coin = @ccf.find(title)
    
    if coin.nil? then
      puts 'warning: build_records error: coin nil for title ' \
          + title.inspect
      return r
    end
    
    puts 'coin: ' + coin.inspect if @debug
    usd_rate = coin.price_usd.to_f      

    paid = ((x.qty.to_f * x.btc_price.to_f) * \
            @ccf.price('bitcoin', x.date_purchased) * \
           @jer.rate(@mycurrency)).round(2)
    
    value_usd = (usd_rate * x.qty.to_f).round(2)
    
    h = {
      title: x.title,         
      rank: coin.rank.to_i,
      qty: "%.2f" % x.qty,
      btc_price:  "%.5f" % x.btc_price,
      paid: "%.0f" % paid
    }
    
    mycurrency = if @mycurrency and @mycurrency != 'USD' then
      
      local_value = ((usd_rate * x.qty.to_f) \
                     * @jer.rate(@mycurrency)).round(2)
      h.merge!(('value_' \
                + @mycurrency.downcase).to_sym => "%.2f" % local_value)
      
      @mycurrency
      
    else
      
      'USD'
      
    end
    
    puts 'local_value: ' + local_value.inspect if @debug
    value = (local_value || value_usd)
    
    h2 = {
      roi: "%s" % (value - paid).round(2),
      pct_roi: "%s" % (100 / (paid / (value - paid))).round(2)
    }
    
    r << h.merge!(h2)
    
  end
  
end
fetch_symbols(coin_names) click to toggle source
# File lib/mycoins.rb, line 280
def fetch_symbols(coin_names)
  
  c = CryptocoinFanboi.new debug: @debug
  
  h = coin_names.inject({}) do |r, name|
    
    puts 'name: ' + name.inspect if @debug
    found = c.find name
    r.merge(name => found.symbol)

  end
  
  FileUtils.mkdir_p File.dirname(@cache_file)
  File.write @cache_file, h.to_yaml    

  return h
  
end
format_portfolio(r, order_by: :rank) click to toggle source
# File lib/mycoins.rb, line 319
def format_portfolio(r, order_by: :rank)
  
  coins = r.records.sort_by {|x| -x[order_by].to_f}.map {|x| x.values}
  coins.reverse! if order_by == :rank
  
  labels = %w(Name Rank Qty btc_price) \
      + ["paid:"]
  labels << "value:" if @mycurrency    
  labels += ['ROI:', 'ROI (%):']
  
  puts 'labels: ' + labels.inspect if @debug
  
  out = "# " + @dx.title + "\n\n"
  out << "last_updated: %s\n\n" % r.datetime
  
  out << format_table(coins, labels: labels)
  
  out << "\nNote: paid and value columns displayed in " + @mycurrency
      
  out << "\n\nBTC price: %.2f USD" % [r.btc_price]
  out << "\nInvested: %.2f %s" % [r.invested, @mycurrency]
  out << "\n\nGross profit: %s %s (%s)" % \
      [c(r.gross_profit), @mycurrency, c(r.pct_gross_profit.to_s + "%")]
  out << "\nLosses: %.2f %s (%.2f%%)" % [r.losses, @mycurrency, r.pct_losses]
  out << "\n\nNet profit: %s %s (%s)" % [c(r.net_profit), @mycurrency, 
                                              c(r.pct_net_profit.to_s + "%")]
  out << "\nCurrent value: %.2f %s (%s BTC)" % [r.value, @mycurrency, 
                                                r.btc_value]
  out
  
end
format_table(source, markdown: markdown, labels: []) click to toggle source
# File lib/mycoins.rb, line 299
def format_table(source, markdown: markdown, labels: [])
  
  s = TableFormatter.new(source: source, labels: labels).display
  
  return s if @colored == false    

  a = s.lines
  
  body = a[3..-2].map do |line|
    
    fields = line.split('|')   
    
    a2 = fields[-3..-1].map {|x| c(x) }
    (fields[0..-4] + a2 ).join('|')  

  end    
  
  (a[0..2] + body + [a[-1]]).join    
end
sum(a, field) click to toggle source
# File lib/mycoins.rb, line 351
def sum(a, field)
  a.inject(0) {|r, x| r + x[field].to_f }
end