Module: Timeprice::Compare
- Defined in:
- lib/timeprice/compare.rb
Overview
Compare combines FX and inflation across two (currency, date) points.
CONVENTION (critical): convert at SOURCE date first, then inflate in destination currency. See README.md “Compare semantics” section.
This preserves purchasing-power equivalence in the destination economy. The naive alternative (inflate in source currency first, then convert at destination date) double-counts source-country inflation because nominal FX rates already absorb relative inflation between the two currencies.
If a future refactor flips the order, the regression test in spec/timeprice/compare_spec.rb will fail.
Constant Summary collapse
- CURRENCY_TO_COUNTRY =
Map ISO currency → CPI country code. Kept as a back-compat alias; the canonical map lives in Supported::CURRENCY_TO_COUNTRY.
Supported::CURRENCY_TO_COUNTRY
Class Method Summary collapse
-
.normalize_fx_date(date) ⇒ Object
If the user gave a year like “2010”, anchor FX to mid-year (2010-06-30).
-
.resolve_points(from, to) ⇒ Object
Coerce both points and resolve to_country.
-
.run(amount:, from:, to:) ⇒ CompareResult
Compare an amount across two (currency, date) points.
Class Method Details
.normalize_fx_date(date) ⇒ Object
If the user gave a year like “2010”, anchor FX to mid-year (2010-06-30). If they gave “YYYY-MM”, anchor to the 15th. Full dates pass through.
97 98 99 100 101 102 103 104 105 |
# File 'lib/timeprice/compare.rb', line 97 def normalize_fx_date(date) s = date.to_s case s when /\A\d{4}\z/ then "#{s}-06-30" when /\A\d{4}-\d{2}\z/ then "#{s}-15" when /\A\d{4}-\d{2}-\d{2}\z/ then s else raise ArgumentError, "Invalid date for compare: #{date.inspect}" end end |
.resolve_points(from, to) ⇒ Object
Coerce both points and resolve to_country. Returns a 5-element tuple.
83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/timeprice/compare.rb', line 83 def resolve_points(from, to) from_point = Point.coerce(from) to_point = Point.coerce(to) from_currency = from_point.currency to_currency = to_point.currency to_country = Supported.country_for_currency(to_currency) || (raise UnsupportedCurrency, to_currency) Supported.country_for_currency(from_currency) || (raise UnsupportedCurrency, from_currency) [from_currency, from_point.date, to_currency, to_point.date, to_country] end |
.run(amount:, from:, to:) ⇒ CompareResult
Compare an amount across two (currency, date) points.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'lib/timeprice/compare.rb', line 45 def run(amount:, from:, to:) from_currency, from_date, to_currency, to_date, to_country = resolve_points(from, to) # Step 1: convert at source date into destination currency. fx_date = normalize_fx_date(from_date) fx_result = Exchange.convert( amount: amount, from: from_currency, to: to_currency, date: fx_date ) converted = fx_result.amount # Step 2: inflate that destination-currency amount from source date to # destination date using destination-country CPI. infl = Inflation.adjust( amount: converted, from: from_date.to_s, to: to_date.to_s, country: to_country ) CompareResult.new( amount: infl.amount, original_amount: amount.to_f, from_currency: from_currency, from_date: from_date.to_s, to_currency: to_currency, to_date: to_date.to_s, country: to_country, fx_rate: fx_result.rate, cpi_ratio: infl.to_index.to_f / infl.from_index, converted_amount: converted, granularity: infl.granularity ) end |