Module: Timeprice::Compare Private

Defined in:
lib/timeprice/compare.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

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.

The supported public entry point is compare. Direct references will move to ‘Timeprice::Internal::Compare` in a future release.

Class Method Summary collapse

Class Method Details

.fx_only_result(amount:, from_point:, to_point:, to_country:, fx_result:) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Same-date branch: no time-elapsed inflation, so the FX leg alone is the answer. Builds a CompareResult with cpi_ratio=1.0.



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/timeprice/compare.rb', line 95

def fx_only_result(amount:, from_point:, to_point:, to_country:, fx_result:)
  CompareResult.new(
    amount: fx_result.amount,
    original_amount: amount.to_f,
    from_currency: from_point.currency,
    from_date: from_point.date.to_s,
    to_currency: to_point.currency,
    to_date: to_point.date.to_s,
    country: to_country,
    fx_rate: fx_result.rate,
    cpi_ratio: 1.0,
    converted_amount: fx_result.amount,
    granularity: fx_result.granularity
  )
end

.resolve_points(from, to) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Coerce both points and resolve to_country.



112
113
114
115
116
117
118
119
120
121
# File 'lib/timeprice/compare.rb', line 112

def resolve_points(from, to)
  from_point = Point.coerce(from)
  to_point   = Point.coerce(to)
  fail UnsupportedCurrency, from_point.currency unless Supported.country_for_currency(from_point.currency)

  to_country = Supported.country_for_currency(to_point.currency)
  fail UnsupportedCurrency, to_point.currency unless to_country

  [from_point, to_point, to_country]
end

.run(amount:, from:, to:) ⇒ CompareResult

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Compare an amount across two (currency, date) points.

Parameters:

  • amount (Numeric)
  • from (Timeprice::Point, Array(String, String))

    source point; accepts a Point or a 2-tuple like ‘[“USD”, “2010”]` or `[“USD”, “2010-06”]`

  • to (Timeprice::Point, Array(String, String))

    destination point

Returns:

Raises:



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
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/timeprice/compare.rb', line 47

def run(amount:, from:, to:)
  from_point, to_point, to_country = resolve_points(from, to)

  # Step 1: convert at source date into destination currency.
  fx_result = Exchange.convert(
    amount: amount,
    from: from_point.currency,
    to: to_point.currency,
    date: from_point.fx_anchor_date
  )
  converted = fx_result.amount

  # Step 2: inflate that destination-currency amount from source date to
  # destination date using destination-country CPI. When both points
  # share a date there's no time-elapsed inflation to apply — short-
  # circuit with a ratio of 1.0 so daily-grain FX dates (which CPI's
  # monthly-max resolution can't accept) still resolve cleanly.
  if from_point.date == to_point.date
    return fx_only_result(
      amount: amount, from_point: from_point, to_point: to_point,
      to_country: to_country, fx_result: fx_result
    )
  end

  infl = Inflation.adjust(
    amount: converted,
    from: from_point.date.to_s,
    to: to_point.date.to_s,
    country: to_country
  )

  CompareResult.new(
    amount: infl.amount,
    original_amount: amount.to_f,
    from_currency: from_point.currency,
    from_date: from_point.date.to_s,
    to_currency: to_point.currency,
    to_date: to_point.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: Granularity.merge(fx_result.granularity, infl.granularity)
  )
end