Class: LedgerGetPrices::GetPrices

Inherits:
Object
  • Object
show all
Defined in:
lib/ledger_get_prices.rb

Overview

Synopsis

Tool that uses Yahoo finance to intelligently generate a ledger price database based on your current ledger commodities and time period.

Ensure that you have set the LEDGER and LEDGER_PRICE_DB environment variables before proceeding. Alternatively, you can make the same addition to your .ledgerrc, so long as running ledger vanilla knows where to get the journal and pricedb.

Options

LEDGER_BASE_CURRENCY: Defaults to USD, change this to your reporting currency. LEDGER_PRICE_DATE_FORMAT: The date format of the outputted pricedb. Defaults to %Y/%m/%d.

Constant Summary collapse

BASE_CURRENCY =

Yahoo finance works best in USD, if the base currency is different we will also store the USD price of that currency to allow for conversion.

ENV['LEDGER_BASE_CURRENCY'] || "USD"
PRICE_DB_PATH =

PRICE_HIST is <v3

ENV['LEDGER_PRICE_DB'] || ENV['PRICE_HIST']
DATE_FORMAT =
ENV['LEDGER_PRICE_DATE_FORMAT'] || "%Y/%m/%d"
PRICE_FORMAT =
"P %{date} %{time} %{symbol} %{price}"
COMMODITY_BLACKLIST =
(ENV['LEDGER_PRICE_COMMODITY_BLACKLIST'] || 'BTC').split(" ")

Class Method Summary collapse

Class Method Details

.existing_pricesArray

We work with the database as an array of price definitions

Returns:

  • (Array)

    an array of formatted prices



44
45
46
47
48
# File 'lib/ledger_get_prices.rb', line 44

def existing_prices
  @existing_prices ||= File.read(PRICE_DB_PATH)
                          .split("\n")
                          .reject { |x| (/^P.*$/ =~ x) != 0 }
end

.new_pricesArray

This method builds a new price database intelligently.

Returns:

  • (Array)

    an array of formatted prices



53
54
55
56
57
58
59
# File 'lib/ledger_get_prices.rb', line 53

def new_prices
  commodities.reduce(existing_prices) do |db, c|
    # `|` is a shortcut for merge
    db | prices_for_symbol(c, start_date: start_date, end_date: end_date)
        .map { |x| price_string_from_result(x, symbol: c) }
  end
end

.price_string_from_result(data, symbol: nil) ⇒ String

Returns:

  • (String)


95
96
97
98
99
100
101
102
103
# File 'lib/ledger_get_prices.rb', line 95

def price_string_from_result(data, symbol: nil)
  raise "Must pass symbol" if symbol.nil?
  PRICE_FORMAT % {
    date: Date.strptime(data.trade_date, '%Y-%m-%d').strftime(DATE_FORMAT),
    time: '23:59:59',
    symbol: (BASE_CURRENCY == 'USD' ? '$' : 'USD'),
    price: (BASE_CURRENCY == symbol ? '$' : symbol)+ data.close
  }
end

.prices_for_symbol(symbol, start_date: start_date, end_date: end_date) ⇒ Array

Returns of YahooFinance results (OpenStruct).

Returns:

  • (Array)

    of YahooFinance results (OpenStruct)



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'lib/ledger_get_prices.rb', line 62

def prices_for_symbol(symbol, start_date: start_date, end_date: end_date) # -> Array
  puts "Getting historical quotes for: #{symbol}"

  if COMMODITY_BLACKLIST.include?(symbol)
    puts "Skipping #{symbol}: blacklisted."
    puts "Use `LEDGER_PRICE_COMMODITY_BLACKLIST` to configure the blacklist."
    puts "BTC is included by default because yahoo doesn't provide a way to " +
        "get historical data for it."
    return []
  end

  result = nil
  quote_strings = possible_quote_strings(commodity: symbol)
  err = nil

  while quote_strings.length > 0 && result.nil?
    begin
      result = YahooFinance::Client.new.historical_quotes(
        quote_strings.shift, start_date: start_date, end_date: end_date, period: :daily)
    rescue OpenURI::HTTPError => e
      err = e
    end
  end

  if result.nil?
    puts "Could not get quotes from Yahoo for: #{symbol}"
    puts "It may be worthwhile getting prices for this manually."
    []
  else result
  end
end

.run!Object

With a bang because it does a file write.



38
39
40
# File 'lib/ledger_get_prices.rb', line 38

def run!
  File.write(PRICE_DB_PATH, new_prices.join("\n"))
end