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 +7 -0
- data/.gitignore +22 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +28 -0
- data/Rakefile +2 -0
- data/bin/getprices +3 -0
- data/ledger_get_prices.gemspec +23 -0
- data/lib/ledger_get_prices/version.rb +3 -0
- data/lib/ledger_get_prices.rb +141 -0
- metadata +96 -0
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
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
data/bin/getprices
ADDED
@@ -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,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: []
|