timeprice 0.3.0 → 0.4.0
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 +4 -4
- data/CHANGELOG.md +33 -0
- data/DATA_LICENSES.md +2 -1
- data/README.md +2 -2
- data/data/cpi/eu.json +23 -1
- data/data/cpi/jp.json +18 -2
- data/data/cpi/uk.json +23 -1
- data/data/cpi/us.json +29 -1
- data/data/cpi/vn.json +362 -34
- data/data/fx/usd/1983.json +7 -8
- data/data/fx/usd/1986.json +7 -8
- data/data/fx/usd/1987.json +7 -8
- data/data/fx/usd/1988.json +7 -8
- data/data/fx/usd/1989.json +7 -8
- data/data/fx/usd/1990.json +7 -8
- data/data/fx/usd/1991.json +7 -8
- data/data/fx/usd/1992.json +7 -8
- data/data/fx/usd/1993.json +7 -8
- data/data/fx/usd/1994.json +7 -8
- data/data/fx/usd/1995.json +7 -8
- data/data/fx/usd/1996.json +7 -8
- data/data/fx/usd/1997.json +7 -8
- data/data/fx/usd/1998.json +7 -8
- data/data/fx/usd/1999.json +266 -525
- data/data/fx/usd/2000.json +262 -517
- data/data/fx/usd/2001.json +261 -512
- data/data/fx/usd/2002.json +262 -514
- data/data/fx/usd/2003.json +262 -514
- data/data/fx/usd/2004.json +266 -522
- data/data/fx/usd/2005.json +264 -521
- data/data/fx/usd/2006.json +262 -514
- data/data/fx/usd/2007.json +262 -514
- data/data/fx/usd/2008.json +263 -516
- data/data/fx/usd/2009.json +263 -516
- data/data/fx/usd/2010.json +265 -523
- data/data/fx/usd/2011.json +264 -521
- data/data/fx/usd/2012.json +263 -516
- data/data/fx/usd/2013.json +262 -514
- data/data/fx/usd/2014.json +262 -514
- data/data/fx/usd/2015.json +263 -516
- data/data/fx/usd/2016.json +264 -521
- data/data/fx/usd/2017.json +262 -514
- data/data/fx/usd/2018.json +262 -514
- data/data/fx/usd/2019.json +262 -514
- data/data/fx/usd/2020.json +264 -518
- data/data/fx/usd/2021.json +265 -523
- data/data/fx/usd/2022.json +264 -521
- data/data/fx/usd/2023.json +262 -514
- data/data/fx/usd/2024.json +263 -516
- data/data/fx/usd/2025.json +5 -5
- data/data/fx/usd/2026.json +5 -5
- data/lib/timeprice/cli/presenters/compare.rb +3 -1
- data/lib/timeprice/cli/presenters/inflation.rb +2 -1
- data/lib/timeprice/compare.rb +2 -1
- data/lib/timeprice/cpi_lookup.rb +7 -5
- data/lib/timeprice/data_loader.rb +1 -1
- data/lib/timeprice/exchange.rb +32 -15
- data/lib/timeprice/granularity.rb +46 -0
- data/lib/timeprice/inflation.rb +4 -17
- data/lib/timeprice/sources.rb +5 -5
- data/lib/timeprice/version.rb +1 -1
- metadata +2 -1
data/data/fx/usd/2025.json
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"schema_version": 2,
|
|
2
3
|
"base": "USD",
|
|
4
|
+
"year": 2025,
|
|
5
|
+
"source": "Frankfurter (ECB) — daily reference rates",
|
|
6
|
+
"updated_at": "2026-05-11",
|
|
3
7
|
"rates": {
|
|
4
8
|
"2025-01-02": {
|
|
5
9
|
"EUR": 0.9689,
|
|
@@ -1276,9 +1280,5 @@
|
|
|
1276
1280
|
"GBP": 0.74264,
|
|
1277
1281
|
"JPY": 156.67
|
|
1278
1282
|
}
|
|
1279
|
-
}
|
|
1280
|
-
"schema_version": 1,
|
|
1281
|
-
"source": "Frankfurter (ECB) — daily reference rates",
|
|
1282
|
-
"updated_at": "2026-05-11",
|
|
1283
|
-
"year": 2025
|
|
1283
|
+
}
|
|
1284
1284
|
}
|
data/data/fx/usd/2026.json
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
{
|
|
2
|
+
"schema_version": 2,
|
|
2
3
|
"base": "USD",
|
|
4
|
+
"year": 2026,
|
|
5
|
+
"source": "Frankfurter (ECB) — daily reference rates",
|
|
6
|
+
"updated_at": "2026-05-11",
|
|
3
7
|
"rates": {
|
|
4
8
|
"2026-01-02": {
|
|
5
9
|
"EUR": 0.85317,
|
|
@@ -441,9 +445,5 @@
|
|
|
441
445
|
"GBP": 0.73472,
|
|
442
446
|
"JPY": 156.76
|
|
443
447
|
}
|
|
444
|
-
}
|
|
445
|
-
"schema_version": 1,
|
|
446
|
-
"source": "Frankfurter (ECB) — daily reference rates",
|
|
447
|
-
"updated_at": "2026-05-11",
|
|
448
|
-
"year": 2026
|
|
448
|
+
}
|
|
449
449
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../formatting"
|
|
4
|
+
require_relative "../../granularity"
|
|
4
5
|
|
|
5
6
|
module Timeprice
|
|
6
7
|
class CLI < Thor
|
|
@@ -35,7 +36,8 @@ module Timeprice
|
|
|
35
36
|
"#{final} in #{@result.to_date}",
|
|
36
37
|
" #{original} (#{@result.from_date})",
|
|
37
38
|
format(" -> %-#{width}s -> %s (%s)", step1, converted, @result.from_date),
|
|
38
|
-
format(" -> %-#{width}s -> %s (%s, %s)", step2, final, @result.to_date,
|
|
39
|
+
format(" -> %-#{width}s -> %s (%s, %s)", step2, final, @result.to_date,
|
|
40
|
+
Granularity.humanize(@result.granularity)),
|
|
39
41
|
]
|
|
40
42
|
end
|
|
41
43
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../formatting"
|
|
4
|
+
require_relative "../../granularity"
|
|
4
5
|
|
|
5
6
|
module Timeprice
|
|
6
7
|
class CLI < Thor
|
|
@@ -27,7 +28,7 @@ module Timeprice
|
|
|
27
28
|
format(" %s %s (%s) -> %s %s (%s)",
|
|
28
29
|
fmt_money(@result.original_amount, @ccy), @ccy, @result.from,
|
|
29
30
|
fmt_money(@result.amount, @ccy), @ccy, @result.to),
|
|
30
|
-
" #{@result.country} · #{@result.granularity} CPI",
|
|
31
|
+
" #{@result.country} · #{Granularity.humanize(@result.granularity)} CPI",
|
|
31
32
|
]
|
|
32
33
|
end
|
|
33
34
|
end
|
data/lib/timeprice/compare.rb
CHANGED
|
@@ -5,6 +5,7 @@ require_relative "supported"
|
|
|
5
5
|
require_relative "point"
|
|
6
6
|
require_relative "inflation"
|
|
7
7
|
require_relative "exchange"
|
|
8
|
+
require_relative "granularity"
|
|
8
9
|
|
|
9
10
|
module Timeprice
|
|
10
11
|
CompareResult = Data.define(
|
|
@@ -70,7 +71,7 @@ module Timeprice
|
|
|
70
71
|
fx_rate: fx_result.rate,
|
|
71
72
|
cpi_ratio: infl.to_index.to_f / infl.from_index,
|
|
72
73
|
converted_amount: converted,
|
|
73
|
-
granularity: infl.granularity
|
|
74
|
+
granularity: Granularity.merge(fx_result.granularity, infl.granularity)
|
|
74
75
|
)
|
|
75
76
|
end
|
|
76
77
|
|
data/lib/timeprice/cpi_lookup.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "errors"
|
|
4
|
+
require_relative "granularity"
|
|
4
5
|
|
|
5
6
|
module Timeprice
|
|
6
7
|
# CpiPoint pairs a CPI index value with the granularity of how it was
|
|
7
|
-
# resolved
|
|
8
|
+
# resolved. See {Granularity} for the full set of possible tags.
|
|
8
9
|
CpiPoint = Data.define(:value, :granularity)
|
|
9
10
|
|
|
10
11
|
# Resolves CPI keys ("YYYY" or "YYYY-MM") to a CpiPoint against a single
|
|
@@ -33,21 +34,22 @@ module Timeprice
|
|
|
33
34
|
private
|
|
34
35
|
|
|
35
36
|
def monthly_or_annual_fallback(month_key)
|
|
36
|
-
return CpiPoint.new(value: @monthly[month_key], granularity:
|
|
37
|
+
return CpiPoint.new(value: @monthly[month_key], granularity: Granularity::MONTHLY) if @monthly.key?(month_key)
|
|
37
38
|
|
|
38
39
|
year = month_key[0, 4]
|
|
39
40
|
raise DataNotFound, missing_message(month_key) unless @annual.key?(year)
|
|
40
41
|
|
|
41
|
-
CpiPoint.new(value: @annual[year], granularity:
|
|
42
|
+
CpiPoint.new(value: @annual[year], granularity: Granularity::MONTHLY_FROM_ANNUAL_FALLBACK)
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def annual_or_monthly_average(year)
|
|
45
|
-
return CpiPoint.new(value: @annual[year], granularity:
|
|
46
|
+
return CpiPoint.new(value: @annual[year], granularity: Granularity::ANNUAL) if @annual.key?(year)
|
|
46
47
|
|
|
47
48
|
months = @monthly.select { |k, _| k.start_with?("#{year}-") }
|
|
48
49
|
raise DataNotFound, missing_message(year) if months.empty?
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
avg = months.values.sum.to_f / months.size
|
|
52
|
+
CpiPoint.new(value: avg, granularity: Granularity::ANNUAL_FROM_MONTHLY_AVG)
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def missing_message(key)
|
|
@@ -9,7 +9,7 @@ module Timeprice
|
|
|
9
9
|
# by setting `TIMEPRICE_DATA_ROOT` in the environment or assigning
|
|
10
10
|
# {DataLoader.data_root=}.
|
|
11
11
|
module DataLoader
|
|
12
|
-
SUPPORTED_SCHEMA_VERSION =
|
|
12
|
+
SUPPORTED_SCHEMA_VERSION = 2
|
|
13
13
|
|
|
14
14
|
DEFAULT_DATA_ROOT = File.expand_path("../../data", __dir__)
|
|
15
15
|
|
data/lib/timeprice/exchange.rb
CHANGED
|
@@ -4,10 +4,11 @@ require "date"
|
|
|
4
4
|
require_relative "errors"
|
|
5
5
|
require_relative "data_loader"
|
|
6
6
|
require_relative "supported"
|
|
7
|
+
require_relative "granularity"
|
|
7
8
|
|
|
8
9
|
module Timeprice
|
|
9
10
|
ExchangeResult = Data.define(
|
|
10
|
-
:amount, :original_amount, :from, :to, :date, :effective_date, :rate
|
|
11
|
+
:amount, :original_amount, :from, :to, :date, :effective_date, :rate, :granularity
|
|
11
12
|
)
|
|
12
13
|
|
|
13
14
|
# Historical FX conversion using bundled per-year USD-base rate files.
|
|
@@ -37,7 +38,7 @@ module Timeprice
|
|
|
37
38
|
|
|
38
39
|
d = parse_date(date)
|
|
39
40
|
|
|
40
|
-
rate, eff_date = resolve_rate(from, to, d)
|
|
41
|
+
rate, eff_date, granularity = resolve_rate(from, to, d)
|
|
41
42
|
ExchangeResult.new(
|
|
42
43
|
amount: amount.to_f * rate,
|
|
43
44
|
original_amount: amount.to_f,
|
|
@@ -45,40 +46,44 @@ module Timeprice
|
|
|
45
46
|
to: to,
|
|
46
47
|
date: d.to_s,
|
|
47
48
|
effective_date: eff_date.to_s,
|
|
48
|
-
rate: rate
|
|
49
|
+
rate: rate,
|
|
50
|
+
granularity: granularity
|
|
49
51
|
)
|
|
50
52
|
end
|
|
51
53
|
|
|
52
|
-
# Returns [rate (Float), effective_date (Date)].
|
|
54
|
+
# Returns [rate (Float), effective_date (Date), granularity (Symbol)].
|
|
55
|
+
# Granularity is :daily when the rate came from a per-date entry, :annual
|
|
56
|
+
# when it came from the per-year `annual` fallback block. Triangulation
|
|
57
|
+
# merges both legs via Granularity.merge (worst-precision-wins).
|
|
53
58
|
# Handles:
|
|
54
59
|
# - identity (from == to)
|
|
55
60
|
# - direct lookup of USD-base rate
|
|
56
61
|
# - inverse (foreign → USD)
|
|
57
62
|
# - triangulation through USD (both legs must resolve to SAME effective date)
|
|
58
63
|
def resolve_rate(from, to, d)
|
|
59
|
-
return [1.0, d] if from == to
|
|
64
|
+
return [1.0, d, Granularity::DAILY] if from == to
|
|
60
65
|
|
|
61
66
|
if from == BASE
|
|
62
|
-
|
|
63
|
-
[rate, eff]
|
|
67
|
+
lookup_usd_base(to, d)
|
|
64
68
|
elsif to == BASE
|
|
65
|
-
rate, eff = lookup_usd_base(from, d)
|
|
66
|
-
[1.0 / rate, eff]
|
|
69
|
+
rate, eff, gran = lookup_usd_base(from, d)
|
|
70
|
+
[1.0 / rate, eff, gran]
|
|
67
71
|
else
|
|
68
72
|
# Triangulation: from → USD → to, both legs at the same effective date.
|
|
69
|
-
usd_to_from, eff_a = lookup_usd_base(from, d)
|
|
70
|
-
usd_to_to, eff_b = lookup_usd_base(to, d)
|
|
73
|
+
usd_to_from, eff_a, gran_a = lookup_usd_base(from, d)
|
|
74
|
+
usd_to_to, eff_b, gran_b = lookup_usd_base(to, d)
|
|
71
75
|
if eff_a != eff_b
|
|
72
76
|
raise DataNotFound,
|
|
73
77
|
"FX triangulation date mismatch for #{from}->#{to} on #{d}: " \
|
|
74
78
|
"USD->#{from} resolved #{eff_a}, USD->#{to} resolved #{eff_b}"
|
|
75
79
|
end
|
|
76
|
-
[usd_to_to / usd_to_from, eff_a]
|
|
80
|
+
[usd_to_to / usd_to_from, eff_a, Granularity.merge(gran_a, gran_b)]
|
|
77
81
|
end
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
# Walk back up to MAX_FALLBACK_DAYS to find a rate
|
|
81
|
-
#
|
|
84
|
+
# Walk back up to MAX_FALLBACK_DAYS to find a daily rate; if none, fall
|
|
85
|
+
# back to the year file's top-level `annual` block.
|
|
86
|
+
# Returns [rate, effective_date, granularity].
|
|
82
87
|
def lookup_usd_base(currency, d)
|
|
83
88
|
(0..MAX_FALLBACK_DAYS).each do |offset|
|
|
84
89
|
candidate = d - offset
|
|
@@ -94,11 +99,23 @@ module Timeprice
|
|
|
94
99
|
rate = rates_for_day[currency]
|
|
95
100
|
next unless rate
|
|
96
101
|
|
|
97
|
-
return [rate.to_f, candidate]
|
|
102
|
+
return [rate.to_f, candidate, Granularity::DAILY]
|
|
98
103
|
end
|
|
104
|
+
|
|
105
|
+
annual_rate = annual_fallback(currency, d.year)
|
|
106
|
+
return [annual_rate, d, Granularity::ANNUAL] if annual_rate
|
|
107
|
+
|
|
99
108
|
raise DataNotFound, "No FX rate for USD->#{currency} on or before #{d}"
|
|
100
109
|
end
|
|
101
110
|
|
|
111
|
+
# Consult the year file's top-level `annual` block. Returns Float or nil.
|
|
112
|
+
def annual_fallback(currency, year)
|
|
113
|
+
year_data = DataLoader.load_fx_year(year)
|
|
114
|
+
year_data.dig("annual", currency)&.to_f
|
|
115
|
+
rescue DataNotFound
|
|
116
|
+
nil
|
|
117
|
+
end
|
|
118
|
+
|
|
102
119
|
def parse_date(date)
|
|
103
120
|
case date
|
|
104
121
|
when Date then date
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Timeprice
|
|
4
|
+
# Closed set of CPI-resolution granularities and the rules for combining /
|
|
5
|
+
# rendering them. Owns the lattice so callers don't hand-maintain it.
|
|
6
|
+
module Granularity
|
|
7
|
+
DAILY = :daily
|
|
8
|
+
MONTHLY = :monthly
|
|
9
|
+
ANNUAL = :annual
|
|
10
|
+
ANNUAL_FROM_MONTHLY_AVG = :annual_from_monthly_avg
|
|
11
|
+
MONTHLY_FROM_ANNUAL_FALLBACK = :monthly_from_annual_fallback
|
|
12
|
+
|
|
13
|
+
# Most-degraded first — `merge` returns the first match.
|
|
14
|
+
# DAILY is the highest-precision FX tag; MONTHLY is the highest-precision
|
|
15
|
+
# CPI tag. Compare uses merge() across both legs, so the most-degraded
|
|
16
|
+
# tag in either leg wins.
|
|
17
|
+
PRECEDENCE = [
|
|
18
|
+
MONTHLY_FROM_ANNUAL_FALLBACK,
|
|
19
|
+
ANNUAL_FROM_MONTHLY_AVG,
|
|
20
|
+
ANNUAL,
|
|
21
|
+
MONTHLY,
|
|
22
|
+
DAILY,
|
|
23
|
+
].freeze
|
|
24
|
+
|
|
25
|
+
HUMAN_LABELS = {
|
|
26
|
+
DAILY => "daily",
|
|
27
|
+
MONTHLY => "monthly",
|
|
28
|
+
ANNUAL => "annual",
|
|
29
|
+
ANNUAL_FROM_MONTHLY_AVG => "annual (avg of months)",
|
|
30
|
+
MONTHLY_FROM_ANNUAL_FALLBACK => "annual (month unavailable)",
|
|
31
|
+
}.freeze
|
|
32
|
+
|
|
33
|
+
module_function
|
|
34
|
+
|
|
35
|
+
# Worst-precision-wins merge across two or more endpoint granularities.
|
|
36
|
+
def merge(*tags)
|
|
37
|
+
PRECEDENCE.find { |t| tags.include?(t) } || MONTHLY
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Human-readable label for CLI output. Falls through to the symbol's
|
|
41
|
+
# string form so an unknown tag still renders something.
|
|
42
|
+
def humanize(tag)
|
|
43
|
+
HUMAN_LABELS.fetch(tag, tag.to_s)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/timeprice/inflation.rb
CHANGED
|
@@ -3,15 +3,11 @@
|
|
|
3
3
|
require_relative "errors"
|
|
4
4
|
require_relative "data_loader"
|
|
5
5
|
require_relative "cpi_lookup"
|
|
6
|
+
require_relative "granularity"
|
|
6
7
|
|
|
7
8
|
module Timeprice
|
|
8
|
-
# Value object returned by Inflation.adjust.
|
|
9
|
-
#
|
|
10
|
-
# granularity is one of:
|
|
11
|
-
# :monthly — both ends resolved on monthly data
|
|
12
|
-
# :annual — at least one end resolved on annual data
|
|
13
|
-
# :annual_from_monthly_avg — at least one end was an annual request resolved
|
|
14
|
-
# by averaging 12 months of monthly data
|
|
9
|
+
# Value object returned by Inflation.adjust. See {Granularity} for the set
|
|
10
|
+
# of possible `granularity` values and the worst-precision-wins merge rule.
|
|
15
11
|
InflationResult = Data.define(
|
|
16
12
|
:amount, :original_amount, :from, :to, :country,
|
|
17
13
|
:from_index, :to_index, :granularity
|
|
@@ -54,7 +50,7 @@ module Timeprice
|
|
|
54
50
|
country: country.to_s.upcase,
|
|
55
51
|
from_index: from_point.value,
|
|
56
52
|
to_index: to_point.value,
|
|
57
|
-
granularity:
|
|
53
|
+
granularity: Granularity.merge(from_point.granularity, to_point.granularity)
|
|
58
54
|
)
|
|
59
55
|
end
|
|
60
56
|
|
|
@@ -68,14 +64,5 @@ module Timeprice
|
|
|
68
64
|
result = adjust(amount: 1.0, from: from, to: to, country: country)
|
|
69
65
|
result.amount - 1.0
|
|
70
66
|
end
|
|
71
|
-
|
|
72
|
-
# If either end fell back to annual_from_monthly_avg, propagate that label;
|
|
73
|
-
# else if either is annual, propagate :annual; else :monthly.
|
|
74
|
-
def merge_granularity(a, b)
|
|
75
|
-
return :annual_from_monthly_avg if a == :annual_from_monthly_avg || b == :annual_from_monthly_avg
|
|
76
|
-
return :annual if a == :annual || b == :annual
|
|
77
|
-
|
|
78
|
-
:monthly
|
|
79
|
-
end
|
|
80
67
|
end
|
|
81
68
|
end
|
data/lib/timeprice/sources.rb
CHANGED
|
@@ -48,10 +48,10 @@ module Timeprice
|
|
|
48
48
|
id: "vn_cpi",
|
|
49
49
|
kind: "cpi",
|
|
50
50
|
country: "VN",
|
|
51
|
-
name: "World Bank
|
|
52
|
-
license: "CC BY 4.0",
|
|
53
|
-
license_url: "https://
|
|
54
|
-
attribution: "
|
|
51
|
+
name: "IMF Data Portal CPI dataflow (monthly primary) + World Bank FP.CPI.TOTL (annual fallback)",
|
|
52
|
+
license: "IMF: free reuse with attribution; World Bank: CC BY 4.0",
|
|
53
|
+
license_url: "https://www.imf.org/external/terms.htm",
|
|
54
|
+
attribution: "Sources: IMF Data Portal CPI dataflow; World Bank FP.CPI.TOTL",
|
|
55
55
|
},
|
|
56
56
|
{
|
|
57
57
|
id: "fx_ecb",
|
|
@@ -66,7 +66,7 @@ module Timeprice
|
|
|
66
66
|
id: "fx_vnd",
|
|
67
67
|
kind: "fx",
|
|
68
68
|
country: "VN",
|
|
69
|
-
name: "World Bank — PA.NUS.FCRF (VND annual average,
|
|
69
|
+
name: "World Bank — PA.NUS.FCRF (VND annual average, annual-granularity fallback)",
|
|
70
70
|
license: "CC BY 4.0",
|
|
71
71
|
license_url: "https://datacatalog.worldbank.org/public-licenses#cc-by",
|
|
72
72
|
attribution: "VND FX: World Bank, PA.NUS.FCRF",
|
data/lib/timeprice/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: timeprice
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick
|
|
@@ -167,6 +167,7 @@ files:
|
|
|
167
167
|
- lib/timeprice/data_loader.rb
|
|
168
168
|
- lib/timeprice/errors.rb
|
|
169
169
|
- lib/timeprice/exchange.rb
|
|
170
|
+
- lib/timeprice/granularity.rb
|
|
170
171
|
- lib/timeprice/inflation.rb
|
|
171
172
|
- lib/timeprice/point.rb
|
|
172
173
|
- lib/timeprice/sources.rb
|