Free Canadian Food Inflation Index — Methodology

Version 1.1 — Hybrid Storage + Multi-Basket + X-13 SA Published at canadianfoodindex.ca


1. Scope

The Free Canadian Food Inflation Index is a publicly accessible measure of Canadian food price inflation, computed as a confidence-weighted blend of three independent sources. Version 1.1 stores raw monthly index levels per snapshot and derives multiple views (basket × adjustment × window × period range) at request time; see § 2.4 for the derivation matrix.

Coverage at v1.1:

Out of scope at v1.1:


2. Data Sources

Three independent sources contribute to the blended reading. Each is documented with its upstream attribution, methodology link, and licence.

2.1 Statistics Canada — CPI Food Component

2.2 Bank of Canada — CPI All-Items (Valet Web Service)

2.3 Internal Price-History Source

2.4 Multi-view derivation matrix

Per ADR-0013 v4, the producer stores raw monthly index levels in every snapshot rather than only a pre-computed YoY value. Consumers derive views from those levels at request time. Four orthogonal dimensions span the view space:

DimensionValuesSource of truth
Basketfood_combined, food_stores, food_restaurantsOne snapshot file per basket
Adjustmentnsa (raw), sa (seasonally adjusted)Each snapshot's levels block carries both
Windowstandard (YoY), trailing3 (3-month-trailing YoY)Derived per request from the stored level series
Period rangeAny [from, to] sub-window of the stored 15-month blockFiltered per request by the /levels endpoint

Per-basket vector ids. Each basket maps to a StatsCan vector id from table 18-10-0004-01:

BasketNSA vectorSA vector / source
food_combinedV41690974V41690915 (StatsCan SA cube 18-10-0006)
food_storesV41690975derived in-house via X-13ARIMA-SEATS from NSA
food_restaurantsV41691046derived in-house via X-13ARIMA-SEATS from NSA

SA provenance. Each SA series declares one of two sa_provenance values:

ProvenanceMeaningBaskets at this version
statscan_publishedFetched from a StatsCan SA cube (table 18-10-0006, vector 41690915 for the combined food rollup). Highest-fidelity SA; matches what StatsCan publishes.food_combined
x13_derivedDerived in-house from the NSA series via X-13ARIMA-SEATS (US Census Bureau, public domain). Stamped with x13_spec_version so a future spec bump is differentiable. StatsCan does not publish SA cubes for the sub-baskets.food_stores, food_restaurants

The /yoy and /levels endpoints surface sa_provenance on every response so a consumer can audit whether the SA they're reading is StatsCan's own or our X-13 derivation.

Storage windows. Each snapshot's levels block carries the most recent 15 months of NSA + SA points (LEVELS_STORAGE_WINDOW_MONTHS = 15). The producer fetches 60 months from upstream per refresh (LEVELS_FETCH_INPUT_WINDOW_MONTHS = 60) so X-13 has enough input history for stable seasonal estimates; only the last 15 land in storage. The 15-month window covers both the standard YoY lookback (12 months) and the 3-month-trailing YoY window (15 months total).

Revision tracking. When the producer re-fetches a period that already has a snapshot, deltas per level point that exceed REVISION_DETECTION_EPSILON = 0.005 are appended to a per-(basket, period) JSONL revision log. Anything below the epsilon is round-trip float noise (StatsCan publishes levels to 1 decimal place; 0.005 is half the smallest publishable digit).


3. Blending Formula

Each source's effective weight in the blend is the product of three multiplicative factors:

3.1 Base weights

Weights sum to 1.0 for readability; the blender re-normalizes by the actual contributing weights, so the absolute scale does not matter — only the ratios.

3.2 Fallback factor

When a source could not fetch fresh data, the fetcher returns a fallback sentinel rather than raising. The blender retains the source in the per-source transparency surface but scales its contribution by:

A fallback source contributes 5% of its base weight. This bounds the contamination from a wild sentinel value: with the three sources defined here, if two are fresh at 3.0% and one falls back to 0.0%, the blend lands in the range 2.88–2.96% depending on which source fell back — within 0.12 percentage points of the all-fresh 3.0% reading regardless.

3.3 Freshness decay

Older readings contribute less. The decay is exponential with a 30-day half-life:

A source refreshed today contributes at full effective weight (factor = 1.0). A source 30 days stale contributes at half effective weight (factor = 0.5). A source 60 days stale contributes at one quarter (factor = 0.25). The 30-day half-life is anchored to the monthly publication cadence of StatsCan and BoC: a source one publication cycle stale deserves half its weight.

3.4 Weighted average

If all effective weights sum to zero, the blended value is 0.0. With the published base weights (0.45, 0.35, 0.20), even an all-fallback scenario still produces a defined blend at 5% effective contribution per source. The zero-weight branch is reachable only when a source's source_name is not in SOURCE_BASE_WEIGHTS (an unknown source rolled in from a corrupted snapshot), in which case its base weight resolves to 0.0 and contributes nothing.

3.5 Source agreement scalar

A 0.0-to-1.0 transform of the variance across NON-FALLBACK sources. Let be the set of sources with :

If fewer than 2 non-fallback sources are present, agreement is 1.0 by definition (nothing to disagree with).

3.6 YoY derivations from stored levels

Given a stored level series for a (basket, adjustment) tuple, the supported window derivations are:

Standard YoY — point-to-point year-over-year:

3-month-trailing YoY — smooths short-run noise by averaging three consecutive months on each side of the 12-month lookback:

where — for the trailing-3 average, .

Single-source derivation. Both /yoy and /levels derive from per_source.statscan_cpi_food.levels exclusively (vector 41690974 NSA / vector 41690915 SA for food_combined; sub-basket vector ids per the per-basket table in § 2.4). The three-source blend (StatsCan + BoC + internal) in §§ 3.1–3.5 above applies ONLY to the headline value served at /series for a single (period, basket). Multi-period derivations cannot blend because BoC publishes all-items CPI (not food specifically) and the internal source reports change-rates rather than a monthly level series. This is a methodology choice surfaced as a transparency note on the filter UI; every reading is labelled with its source.

Both derivations require contiguous monthly periods across the lookback window and a strictly positive anchor (denominator). If either guard fails, the /yoy endpoint returns 422 YOY_DERIVATION_FAILED rather than a misleading number.

The same stored levels back BOTH window choices for BOTH adjustment series — no pre-computation, no separate storage, no rebuild required when a new window definition is added.


4. Worked Example

Suppose for period 2026-04 basket food_combined, the three sources return:

SourceValueFreshness lagFallback?
StatsCan CPI Food3.205 daysNo
Bank of Canada CPI2.805 daysNo
Internal price history4.101 dayNo

Effective weights

Blended value

(3.20 × 0.401 + 2.80 × 0.312 + 4.10 × 0.195) / 0.908 = (1.283 + 0.874 + 0.800) / 0.908 = 2.957 / 0.9083.26%

Source agreement

A reading where sources agree to within 1 percentage point yields an agreement scalar near 1.0 — high confidence the blended value is methodology-stable.


5. Source Agreement Interpretation

The source_agreement scalar is the headline trustworthiness signal. Consumers use it to gauge whether to act on the blended value or to dig into per-source contributions before making a decision:

AgreementInterpretationConsumer action
≥ 0.95Sources concur within ~3 percentage points; blended value is methodology-stableUse blended value directly
0.80–0.95Material disagreement (~5–10 percentage points); blended value smooths over a real methodology gapRead per-source values; understand which source drives the divergence
< 0.80Severe disagreement (10+ percentage points); blended value may obscure methodology disagreementTreat blended value as provisional; investigate via per-source detail

The per-source contribution dictionary in the API response always carries every source (including fallbacks) with its raw value and freshness timestamp so consumers can audit the blend at any agreement level.


6. Coverage Gaps

This section documents what v1.1 does NOT do, so consumers and reviewers know the boundaries.


7. Refresh Cadence

SourceRefresh triggerTypical cadence
StatsCan CPI FoodStatistics Canada monthly CPI release~Third Tuesday of the following month
Bank of Canada CPIBoC Valet update following StatsCanSame day as StatsCan CPI release or shortly after
Internal price historyDaily collection pipelineDaily (aggregated to the consumer's queried period)
Blended snapshotOperator-run refresh command after upstream sources update≤ 7 days after StatsCan monthly publication; sooner if dispatched manually

The recorded_at field on every snapshot timestamps when the blend was computed. A consumer reading the API can compute snapshot staleness directly.


8. Reproducibility — How to Recreate One Blended Value

A third-party reviewer should be able to reproduce a single blended value from the three raw upstream sources within 30 minutes using only:

Step-by-step

  1. Pick a period. Choose a month in canonical grammar (e.g., 2026-04) for which the published snapshot is available at canadianfoodindex.ca/api/v1/inflation-index/series?period=2026-04&basket=food_combined
  2. Pull the StatsCan reading. Navigate to Statistics Canada Table 18-10-0004-01. Filter to the food component for the chosen period. Record the value
  3. Pull the BoC reading. Navigate to Bank of Canada Valet Web Service, series V41690973. Filter to the chosen period. Record the value
  4. Pull the internal reading. Read the value field at the path per_source.internal_price_history.value in the /series JSON response. (This is the transparent disclosure — the internal source is documented in this methodology and the per-product contributing data is available on request to peer reviewers)
  5. Compute lag in days between each source's freshness_timestamp and the snapshot's recorded_at timestamp
  6. Compute effective weights for each source using the formula in section 3
  7. Compute the weighted average using the formula in section 3.4
  8. Compare to the published blended_value. Should match to within rounding (4 decimal places of agreement)

Discrepancies between your reproduction and the published blended_value indicate either a bug in the blender (please report) or a stale snapshot (re-fetch).


9. Versions


10. Contact + Peer Review

Reproducibility commitment

Per section 8, any reader should be able to reproduce a published blended value from raw upstream sources within 30 minutes using a browser and a spreadsheet. The methodology constants used by the production blender (per-source base weights, fallback factor, freshness half-life, agreement variance scale) are documented in section 3 and re-derivable against the per-source contribution dictionary on every API response.

Interactive worked example

Move the controls below to see how the blended value and source-agreement scalar change. Export the resulting equation as LaTeX or Markdown for citation in your own document.

Blended: 3.2563%
Source agreement: 0.9970 (3 of 3 fresh)
Export as LaTeX
\text{blended} = \frac{3.20 \cdot 0.401 + 2.80 \cdot 0.312 + 4.10 \cdot 0.195}{0.908} = 3.2563\%
Export as Markdown
## Interactive worked example

| Source | Value | Lag (days) | Fallback | Effective weight |
|---|---|---|---|---|
| statscan_cpi_food | 3.20% | 5 | no | 0.4009 |
| bank_of_canada_cpi | 2.80% | 5 | no | 0.3118 |
| internal_price_history | 4.10% | 1 | no | 0.1954 |

**Blended value:** 3.2563%
**Source agreement:** 0.9970

Read in plain language

Two cluster pages restate the methodology above in a more accessible register, indexed separately for AI Overview citation.

Looking for something else?