ledger_get_prices 0.0.4

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: faf17809292e7310521c27fd452006a12c4e585d
4
+ data.tar.gz: a89bdaa59b678bf5148f2b70fbc869c4df30c88d
5
+ SHA512:
6
+ metadata.gz: 4ef4c4c623b7b9bbd5bf7cd72629301c67ddf76c0f623445ef3b8f4ec84e90e227f648f99dcf1246b4e24440d5ca6b9143d4b9dec8969930b0b0db3ad2bdb502
7
+ data.tar.gz: c3eb9534015dff5466e9196bd92b36570c65d4dab13d24321538dfeacec37639b44dc0cf82124880acba969e43718e20925d1dd7356299b25da07493c0df6b4f
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ledger_get_prices.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Nathan Kot
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ ## ledger-get-prices
2
+
3
+ It's the end of financial year, and now that I'm accounting with NZD,USD,JPY,BTC I've spent some time to automate the
4
+ pricedb process. Enjoy :) PR's welcome.
5
+
6
+ # Synopsis
7
+
8
+ _Tool that uses Yahoo finance to intelligently generate
9
+ a ledger price database based on your current ledger
10
+ commodities and time period._
11
+
12
+ Ensure that you have set the `LEDGER` and `LEDGER_PRICE_DB`
13
+ environment variables before proceeding. Alternatively, you
14
+ can make the same addition to your `.ledgerrc`, so long as
15
+ running `ledger` vanilla knows where to get the journal and
16
+ pricedb.
17
+
18
+ # Environment Variables
19
+
20
+ * __LEDGER_BASE_CURRENCY__: Defaults to USD, change this to your reporting currency.
21
+ * __LEDGER_PRICE_DATE_FORMAT__: The date format of the outputted pricedb. Defaults to +%Y/%m/%d+.
22
+
23
+ # Usage
24
+
25
+ ```sh
26
+ gem install ledger-get-prices
27
+ getprices # This will WRITE to your LEDGER_PRICE_DB file.
28
+ ```
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/getprices ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env ruby
2
+ require 'ledger_get_prices'
3
+ LedgerGetPrices::GetPrices.run!
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ledger_get_prices/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "ledger_get_prices"
8
+ spec.version = LedgerGetPrices::VERSION
9
+ spec.authors = ["Nathan Kot"]
10
+ spec.email = ["nk@nathankot.com"]
11
+ spec.summary = 'Intelligently update your ledger pricedb'
12
+ spec.homepage = 'https://github.com/nathankot/ledger-get-prices'
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_runtime_dependency "yahoo-finance"
23
+ end
@@ -0,0 +1,3 @@
1
+ module LedgerGetPrices
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,141 @@
1
+ require "ledger_get_prices/version"
2
+ require 'date'
3
+ require 'open-uri'
4
+ require 'yahoo-finance'
5
+
6
+ module LedgerGetPrices
7
+
8
+ # = Synopsis
9
+ #
10
+ # Tool that uses Yahoo finance to intelligently generate
11
+ # a ledger price database based on your current ledger
12
+ # commodities and time period.
13
+ #
14
+ # Ensure that you have set the +LEDGER+ and +LEDGER_PRICE_DB+
15
+ # environment variables before proceeding. Alternatively, you
16
+ # can make the same addition to your +.ledgerrc+, so long as
17
+ # running +ledger+ vanilla knows where to get the journal and
18
+ # pricedb.
19
+ #
20
+ # = Options
21
+ #
22
+ # +LEDGER_BASE_CURRENCY+: Defaults to USD, change this to your reporting currency.
23
+ # +LEDGER_PRICE_DATE_FORMAT+: The date format of the outputted pricedb. Defaults to +%Y/%m/%d+.
24
+ class GetPrices
25
+ class << self
26
+
27
+ # Yahoo finance works best in USD, if the base currency is
28
+ # different we will also store the USD price of that currency
29
+ # to allow for conversion.
30
+ BASE_CURRENCY = ENV['LEDGER_BASE_CURRENCY'] || "USD"
31
+
32
+ PRICE_DB_PATH = ENV['LEDGER_PRICE_DB'] || ENV['PRICE_HIST'] # PRICE_HIST is <v3
33
+ DATE_FORMAT = ENV['LEDGER_PRICE_DATE_FORMAT'] || "%Y/%m/%d"
34
+ PRICE_FORMAT = "P %{date} %{time} %{symbol} %{price}"
35
+ COMMODITY_BLACKLIST = (ENV['LEDGER_PRICE_COMMODITY_BLACKLIST'] || 'BTC').split(" ")
36
+
37
+ # With a bang because it does a file write.
38
+ def run!
39
+ File.write(PRICE_DB_PATH, new_prices.join("\n"))
40
+ end
41
+
42
+ # We work with the database as an array of price definitions
43
+ # @return [Array] an array of formatted prices
44
+ def existing_prices
45
+ @existing_prices ||= File.read(PRICE_DB_PATH)
46
+ .split("\n")
47
+ .reject { |x| (/^P.*$/ =~ x) != 0 }
48
+ end
49
+
50
+ # This method builds a new price database intelligently.
51
+ #
52
+ # @return [Array] an array of formatted prices
53
+ def new_prices
54
+ commodities.reduce(existing_prices) do |db, c|
55
+ # `|` is a shortcut for merge
56
+ db | prices_for_symbol(c, start_date: start_date, end_date: end_date)
57
+ .map { |x| price_string_from_result(x, symbol: c) }
58
+ end
59
+ end
60
+
61
+ # @return [Array] of YahooFinance results (OpenStruct)
62
+ def prices_for_symbol(symbol, start_date: start_date, end_date: end_date) # -> Array
63
+ puts "Getting historical quotes for: #{symbol}"
64
+
65
+ if COMMODITY_BLACKLIST.include?(symbol)
66
+ puts "Skipping #{symbol}: blacklisted."
67
+ puts "Use `LEDGER_PRICE_COMMODITY_BLACKLIST` to configure the blacklist."
68
+ puts "BTC is included by default because yahoo doesn't provide a way to " +
69
+ "get historical data for it."
70
+ return []
71
+ end
72
+
73
+ result = nil
74
+ quote_strings = possible_quote_strings(commodity: symbol)
75
+ err = nil
76
+
77
+ while quote_strings.length > 0 && result.nil?
78
+ begin
79
+ result = YahooFinance.historical_quotes(
80
+ quote_strings.shift, start_date: start_date, end_date: end_date, period: :daily)
81
+ rescue OpenURI::HTTPError => e
82
+ err = e
83
+ end
84
+ end
85
+
86
+ if result.nil?
87
+ puts "Could not get quotes from Yahoo for: #{symbol}"
88
+ puts "It may be worthwhile getting prices for this manually."
89
+ []
90
+ else result
91
+ end
92
+ end
93
+
94
+ # @return [String]
95
+ def price_string_from_result(data, symbol: nil)
96
+ raise "Must pass symbol" if symbol.nil?
97
+ PRICE_FORMAT % {
98
+ date: Date.strptime(data.trade_date, '%Y-%m-%d').strftime(DATE_FORMAT),
99
+ time: '23:59:59',
100
+ symbol: (BASE_CURRENCY == 'USD' ? '$' : 'USD'),
101
+ price: (BASE_CURRENCY == symbol ? '$' : symbol)+ data.close
102
+ }
103
+ end
104
+
105
+ protected
106
+
107
+ # Start date is either the latest price record or the earliest
108
+ # ever transaction.
109
+ # @return [Date]
110
+ def start_date
111
+ @start_date ||= existing_prices.map { |x| Date.strptime(x.split(" ")[1], DATE_FORMAT) }.max || begin
112
+ stats = `ledger stats` # Most compact way to retrieve this data
113
+ date_str = /Time\speriod:\s*([\d\w\-]*)\s*to/.match(stats)[1]
114
+ return Date.strptime(date_str, "%y-%b-%d")
115
+ end
116
+ end
117
+
118
+ # End date is today, wish I can see the future but unfortunately..
119
+ # @return [Date]
120
+ def end_date
121
+ @end_date ||= Date.new()
122
+ end
123
+
124
+ # Try the commodity as a currency first, before trying it as a stock
125
+ # @return [Array<String>] Possible Yahoo finance compatible quote strings
126
+ def possible_quote_strings(commodity: nil)
127
+ raise "No commodity given" if commodity.nil?
128
+ ["#{commodity}=X", "USD#{commodity}=X", "#{commodity}"]
129
+ end
130
+
131
+ def commodities
132
+ # All the commodities we care about.
133
+ @commodities ||= `ledger commodities`.split("\n").reject { |x| x == "$" }.tap do |c|
134
+ c << BASE_CURRENCY if BASE_CURRENCY != 'USD'
135
+ end
136
+ end
137
+
138
+ end
139
+ end
140
+
141
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ledger_get_prices
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Kot
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-05-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yahoo-finance
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - nk@nathankot.com
58
+ executables:
59
+ - getprices
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - bin/getprices
69
+ - ledger_get_prices.gemspec
70
+ - lib/ledger_get_prices.rb
71
+ - lib/ledger_get_prices/version.rb
72
+ homepage: https://github.com/nathankot/ledger-get-prices
73
+ licenses:
74
+ - MIT
75
+ metadata: {}
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubyforge_project:
92
+ rubygems_version: 2.2.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: Intelligently update your ledger pricedb
96
+ test_files: []