TeX 52.9%
Python 45.7%
Shell 1.4%
1<!-- Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca -->2# ⚖️ Does the Sales Comparison Approach Actually Work?34**UQO Working Paper No. 16** — *A Large-Scale Point-in-Time Validation of Comparable Selection, Price Adjustments, and5Value Reconciliation — Evidence from 391,000 Single-Family Transactions in Quebec, 2021–2026*67[-b31b1b?logo=latex&logoColor=white)](paper/main.pdf)8[](pipeline/)9[](docs/PROTOCOLE.md)10[](pipeline/)11[](https://uqo.ca)12[](mailto:simon-pierre.boucher@uqo.ca)1314> **TL;DR** — The sales comparison approach rests on professional convention: three comparables, a short window, a15> small radius, an adjustment grid, a reconciliation rule. We backtest 2,256 configurations of the approach, point in16> time, on 189,354 masked target sales drawn from 390,996 Quebec single-family transactions matched to the assessment17> roll (configurations chosen on 2024, evaluated on 2025–2026). **Adjustments dominate** (median absolute error of the18> five nearest sales: 18.1% time-only → 13.0% with a hedonic grid); **similar beats near** (selecting physically similar19> sales gives the same gain with no adjustment); **the optimal number of comparables is ~20–30, not 3**; windows should20> be wide, radius barely matters, weighting and reconciliation matter little. The best transparent rule reaches21> **11.1%** out of sample versus 10–11% for a quarterly-retrained gradient-boosting model: the cost of transparency is22> small, and accuracy is governed by submarket liquidity.2324---2526## 📌 Status2728| Item | Value |29|---|---|30| Version | 1.0 (October 2026) |31| SSRN | abstract **7626720** (PRELIMINARY_UPLOAD, 2026-10-17) — uploaded PDF carries the old cover "Working Paper No. 11"; **re-upload `paper/main.pdf` (No. 16)** |32| Journal | — |33| Code & data | pipeline in `pipeline/`, run on the MacLustr node M2U64; the Vrai Prix database snapshot is not redistributed |3435## 🎯 What this paper does3637For every target sale the price is masked and the approach is applied with only the information available on the38valuation date: candidate comparables are selected (nearest / most similar / compromise, k ∈ {1 … 50}, radius d,39window T), adjusted (none / time only / stylized conventional grid / hedonic grid estimated quarterly / assessment40ratio), weighted and reconciled (mean, median, weighted). Error surfaces Error(k, d, T) are mapped, the best41configuration is chosen on the 2024 validation year and evaluated out of sample on 2025–2026 against hedonic,42gradient-boosting, assessment-ratio and hybrid benchmarks. Accuracy is then decomposed by submarket liquidity, region,43price stratum and property type.4445## 🏆 Headline results4647| Object | Estimate (test 2025–2026, MdAPE) |48|---|---|49| 5 nearest sales, time adjustment only (M2) | 18.1% |50| 5 nearest sales, hedonic adjustment grid (M7) | 13.0% |51| 5 most similar sales, no physical adjustment | ≈ same gain as the grid |52| Number of comparables with a grid, k = 1 → 3 → 5 → 10 → 20–30 | 16.5% → 13.8% → 13.0% → 12.4% → minimum |53| Best transparent rule chosen in 2024 (M0: compromise, k = 30, ≤ 25 km, 24 months, hedonic grid) | **11.1%** |54| Assessment-ratio comparable (M13) | 11.5% |55| Quarterly-retrained GBM using assessed value (M11b) / hybrid (M12) | ≈ 10–11% |56| Liquidity effect (M7): > 200 sales within 2 km / year vs < 20 | 10.2% vs 25.3% |5758## 🗂 Repository layout5960```61sales-comparison-validation/62├── pipeline/ # 01_dataset → 02_index → 03_models → 04_backtest (+04b_gather) → 05_analyze → 06_report63├── run_all.sh # full chain on the compute node (CV_VERSION=YYYY-MM-DD)64├── run_distributed.sh # 15-shard backtest across 5 MacLustr nodes, gather on M2U6465├── make_pdf.sh # pull out/<version>/ + docs/ from M2U64, compile docs/RAPPORT.pdf (French report)66├── docs/ # PROTOCOLE.md (protocol), RAPPORT.md/.pdf (French results report), README-fr.md67├── ref/ # MUN.csv — municipality → MRC → region (MAMH)68├── out/<version>/ # sales.parquet, index_asof.npy, models_*.json, est_E*.npy, tables/, fig/, results.json — not committed69├── data/ # vraiprix-<date>.db snapshot (node only) — not committed70└── paper/ # main.tex + sections/ + tables/ (generated) + figures/ → main.pdf71```7273## 🚀 Quick start7475```bash76rsync -a --exclude out --exclude data --exclude .git ./ M2U64:comparables-validation/77ssh M2U64 'cd ~/comparables-validation && CV_VERSION=2026-10-10 ./run_all.sh' # ≈ 30 min (or ./run_distributed.sh)78./make_pdf.sh # pull out/ + docs/, build RAPPORT.pdf79cd paper && python3 make_paper_tables.py && make # tables + numbers.tex macros → main.pdf80```8182Variables: `CV_VERSION` (output folder), `CV_SAMPLE=3000` (target subsample for a dry run), `CV_PROCS` (processes, 20).83Environment on M2U64: `~/uqo-eval-terrains/env` (Python 3.14, pandas, numpy, scipy, lightgbm, matplotlib).8485## 🗃 Data8687Snapshot of the Vrai Prix production database (2026-10-10): 766,596 residential transactions and 3.75 M assessment-roll88units. Single-family sales matched to the roll, cleaned by logged rules R0–R11 (`out/<v>/exclusions.csv`):89456,950 → **390,996** sales (2021-01-05 → 2026-09-29), split train 201,642 / validation 64,795 (2024) / test 124,55990(2025–2026). Median price $400,000. All numbers in the paper are macros in `paper/tables/numbers.tex`, regenerated by91`paper/make_paper_tables.py` from `out/<version>/`.9293## 🔬 Methodology94951. **Point-in-time index and quarterly hedonic grids** (`02_index.py`, `03_models.py`) — nothing after the valuation date is used.962. **Backtest of 2,256 configurations** × benchmarks on 189,354 targets (`04_backtest.py`, sharded; `04b_gather.py`).973. **Selection on 2024, evaluation on 2025–2026**; error surfaces, marginal effects of each rule, conditional rules by stratum (`05_analyze.py`).984. **Decomposition** by liquidity, region, price, type; stability over time; prediction intervals (`06_report.py`, `paper/make_paper_tables.py`).99100## 🖼 Figures & tables inventory101102| Figure | Content |103|---|---|104| `f1_mdape_vs_k` | error vs number of comparables, by adjustment |105| `f2_surface_d_T` | error surface over radius and window |106| `f3_ajustements` | contribution of adjustment types |107| `f4_competition` | comparables vs hedonic / GBM / ratio / hybrid |108| `f5_densite`, `f6_k_par_densite` | accuracy and optimal k by submarket liquidity |109| `f7_indice` | point-in-time price index |110111Tables (`paper/tables/*.tex`, 28 generated): descriptives, exclusions, selection, adjustments, grid, weighting,112surfaces, competition, density, regions, strata, stability, intervals, conditional rules, hypotheses H2–H5.113114## ⚠️ Limitations115116- Adjustments simulated on observable characteristics (area, lot, age, storeys, attachment, assessed value); unobserved117 condition and quality adjustments made by appraisers are not modelled.118- Single-family houses in Quebec only; results for condos/plexes not covered.119- Registry prices may include non-arm's-length transfers surviving the screens.120121## 📝 Citation122123```bibtex124@techreport{boucher2026salescomparison,125 author = {Boucher, Simon-Pierre},126 title = {Does the Sales Comparison Approach Actually Work? A Large-Scale Point-in-Time Validation of Comparable Selection, Price Adjustments, and Value Reconciliation},127 institution = {Universit\'e du Qu\'ebec en Outaouais, D\'epartement des sciences administratives},128 type = {Working Paper},129 number = {16},130 year = {2026},131 month = {October}132}133```134135## 👤 Author & contact136137**Simon-Pierre Boucher** — Département des sciences administratives,138Université du Québec en Outaouais (UQO), Gatineau, QC.139📧 [simon-pierre.boucher@uqo.ca](mailto:simon-pierre.boucher@uqo.ca)140141## 🗒 Notes142143- Numbering: issued on 2026-10-10 as "Working Paper No. 11" (number already used by *Half a Million Prices, Twenty144 Models*); renumbered **16** on 2026-10-17. The SSRN upload 7626720 predates the renumbering.145- Deviation from `Recherche-UQO/HARMONISATION.md` §2: the pipeline keeps `pipeline/` (not `scripts/`) and `out/` (not146 `results/`) because the same tree runs on M2U64 under `~/comparables-validation` with these paths.147- French protocol and report in `docs/` (`PROTOCOLE.md`, `RAPPORT.md/.pdf`); original French README in `docs/README-fr.md`.148- Per-node copies of the working tree pulled on 2026-10-10 (`M*omparables-validation/`) were removed from the repository149 and archived in `Recherche-UQO/_archives/sales-comparison-node-copies-20261010/`.150