col: simulator auto-adjusts spending to local prices via Numbeo+Expatistan
The Monte Carlo used to compare jurisdictions at a flat London-equivalent spend, which silently overstated the cost-of-living for any move to a cheaper region. Now every cross-jurisdiction simulation auto-scales spending_gbp by the real Numbeo/Expatistan ratio between the user's baseline city and the target city. Architecture: - fire_planner/col/baseline.py — 22 cities with headline Numbeo data (source URLs + snapshot dates embedded) — fallback when scraper fails - col/numbeo.py + col/expatistan.py — httpx async scrapers, regex-parsed, polite 1.1s rate-limit, EUR/USD anchored - col/cache.py — PG-backed cache (col_snapshot table, 1-year TTL) - col/service.py — sync compute_col_ratio() for the simulator; async lookup_city_cached() with source reconciliation for the refresh CronJob - alembic 0005 — col_snapshot table, UNIQUE(city_slug, source_name) Simulator wiring: - SimulateRequest gains col_auto_adjust=True (default), col_baseline_city, col_target_city. Defaults pick the jurisdiction's representative city. - _resolve_col_adjustment scales spending_gbp before path-building. - SimulateResult surfaces col_multiplier_applied + col_adjusted_spending_gbp. CLIs: - python -m fire_planner col-seed — loads BASELINES into col_snapshot (post-migration seed step) - python -m fire_planner col-refresh-stale --within-days 7 — used by the weekly fire-planner-col-refresh CronJob 268 tests pass. Mypy strict + ruff clean. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
70101c836c
commit
e72fd22a17
14 changed files with 1641 additions and 6 deletions
36
fire_planner/col/__init__.py
Normal file
36
fire_planner/col/__init__.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""Cost-of-living module — feeds the simulator with real per-city spend ratios.
|
||||
|
||||
The simulator's `spending_gbp` is denominated in the user's BASELINE city
|
||||
(typically London). When a scenario moves the user to a different TARGET
|
||||
city, this module returns the ratio `target_total / baseline_total` so
|
||||
the simulator can scale `spending_gbp` to local prices before running
|
||||
paths.
|
||||
|
||||
Phase 1 (current): hand-curated baselines from Numbeo public pages, with
|
||||
source URLs and fetch dates embedded so future-us can refresh by hand.
|
||||
Phase 2 (planned): live scrapers for Numbeo + Expatistan, DB cache with
|
||||
30-day TTL, nightly refresh CronJob.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
from fire_planner.col.models import CategoryBreakdown, CityCostIndex, ColSource
|
||||
from fire_planner.col.service import (
|
||||
JURISDICTION_REPRESENTATIVE_CITY,
|
||||
compute_col_ratio,
|
||||
lookup_city,
|
||||
lookup_city_cached,
|
||||
reconcile_sources,
|
||||
representative_city_for,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"CategoryBreakdown",
|
||||
"CityCostIndex",
|
||||
"ColSource",
|
||||
"JURISDICTION_REPRESENTATIVE_CITY",
|
||||
"compute_col_ratio",
|
||||
"lookup_city",
|
||||
"lookup_city_cached",
|
||||
"reconcile_sources",
|
||||
"representative_city_for",
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue