# Vrai-Prix **La vraie valeur de votre propriété, sans boîte noire.** Moteur d'évaluation immobilière **transparent** pour le Québec — **3 747 008 propriétés**, **745 119 ventes réelles (2021-2026)**, chaque calcul montré en entier. ![Propriétés](https://img.shields.io/badge/propri%C3%A9t%C3%A9s-3%C2%A0747%C2%A0008-ff5148?style=flat-square) ![Ventes](https://img.shields.io/badge/ventes_r%C3%A9elles-745%C2%A0119-ff5148?style=flat-square) ![Municipalités](https://img.shields.io/badge/municipalit%C3%A9s-1%C2%A0097-ff5148?style=flat-square) ![MdAPE](https://img.shields.io/badge/MdAPE-11,0%25-9e2a25?style=flat-square) ![Ratio médian](https://img.shields.io/badge/ratio_m%C3%A9dian-0,995-9e2a25?style=flat-square) ![Appariement](https://img.shields.io/badge/fusion_spatiale-99,88%25-9e2a25?style=flat-square) ![URLs indexables](https://img.shields.io/badge/URLs_indexables-3,76_M-9e2a25?style=flat-square) ![Tests](https://img.shields.io/badge/tests-15%2F15-141814?style=flat-square) ![Next.js](https://img.shields.io/badge/Next.js-16-141814?style=flat-square&logo=nextdotjs) ![TypeScript](https://img.shields.io/badge/TypeScript-strict-141814?style=flat-square&logo=typescript) ![Tailwind](https://img.shields.io/badge/Tailwind-4-141814?style=flat-square&logo=tailwindcss) ![SQLite](https://img.shields.io/badge/SQLite-FTS5-141814?style=flat-square&logo=sqlite) ![iOS](https://img.shields.io/badge/iOS-SwiftUI-141814?style=flat-square&logo=apple) **En production : [www.vrai-prix.com](https://www.vrai-prix.com)**
![Aperçu de Vrai-Prix](docs/screenshot.png) --- ## Sommaire - [Ce que fait Vrai-Prix](#ce-que-fait-vrai-prix) - [Fonctionnalités](#fonctionnalités) - [Visite guidée](#visite-guidée) - [Les chiffres qui comptent](#les-chiffres-qui-comptent) - [Architecture](#architecture) - [Le moteur d'estimation](#le-moteur-destimation-srclibenginets) - [Ka — l'agent IA](#ka--lagent-ia) - [SEO programmatique](#seo-programmatique--376-m-durls-indexables) - [Structure du projet](#structure-du-projet) - [Endpoints](#endpoints) - [Démarrage local](#démarrage-local) - [Déploiement](#déploiement) - [Développement remote-first](#développement-remote-first) - [Design](#design) - [Auteur](#auteur) ## Ce que fait Vrai-Prix Vrai-Prix estime la valeur marchande de **n'importe quelle propriété du Québec** (unifamiliale, plex, condo, chalet, terrain) et montre *pourquoi* — **chaque comparable, chaque ajustement en dollars**, la pondération du modèle, la fourchette et l'indice de confiance. Le différenciateur est la **transparence totale** : pas de score magique, **pas de boîte noire**. La méthodologie complète (normes **IAAO**) est publiée intégralement sur le site. Sous le capot : les **6 millésimes (2021-2026)** des rôles d'évaluation foncière géoréférencés du Québec (**MAMH**, données ouvertes) — soit **22 150 285 observations** — fusionnés spatialement avec **745 119 transactions réelles** (**99,88 %** d'appariement, distance médiane **0,6 m**), alimentant un **modèle hédonique LightGBM** et un **moteur de comparables ajustés**. ## Fonctionnalités | Fonctionnalité | Description | |---|---| | 🔎 **Estimation par adresse** | Recherche plein-texte (**FTS5**) parmi **3 747 008 unités d'évaluation**, tolérante aux fautes | | 🧮 **Plan B sans adresse** | **Estimation manuelle** (municipalité + caractéristiques) quand la propriété n'est pas au registre | | 💰 **Estimation hybride** | **65 % modèle hédonique + 35 % comparables ajustés** | | 📊 **Fourchette + confiance** | **P10–P90** par régression quantile, indice **A–D** obligatoire | | 🏠 Portrait de la propriété | Façade SVG dessinée par les données du registre, terrain à l'échelle, composition de la valeur | | 🗺️ **Vrai-Prix Maps** | Recherche par **carte 3D** (Mapbox GL via le framework partagé **Ka Maps**) — grappes de valeurs estimées pilotées par le viewport | | 📐 Plan des comparables | Carte « plan d'arpenteur » maison (SVG pur, zéro tuile) — azimut et distance réels | | 📈 Historique 2021→2026 | **Six millésimes** d'estimations par propriété, badges YoY | | 🤖 **Ka · agent IA** | Agent conversationnel (**Claude**) branché sur **12 outils serveur** : évaluation, comparables, stats, dossier d'expert | | 🏢 Parc immobilier | Évaluation multi-adresses agrégée (jusqu'à **40 propriétés**) + rapport consolidé | | 📄 Rapports PDF | Standard (3 p.), professionnel bancaire (6 p.), parc (n+1 p.), statistique provincial — **100 % vectoriels** | | 📉 **Statistiques** | Valeur totale de la province (**2,01 billions $**), palmarès des **200** municipalités | | 🏙️ Pages municipalités | **1 097** pages programmatiques (médiane, croissance, médianes par type) + **~5 900** pages municipalité × type | | 🔑 KA ID | Connexion unique du **Groupe KA** (SSO signé via le hub groupe-ka.com, session sans base d'utilisateurs) | | 🌐 Bilingue | **FR** (défaut) / **EN**, bascule instantanée côté client | | 📱 App iOS | Compagnon natif **SwiftUI** (onglet carte MapKit) — dépôt séparé | | ⚖️ **Méthodologie transparente** | Loi 25, sélecteur de témoins, méthodologie publique intégrale (7 sections, normes **IAAO**) | ## Visite guidée | | | |---|---| | **Fiche d'estimation** — valeur, fourchette honnête, écart vs rôle, indice de confiance | **Portrait de la propriété** — bâtiment, terrain à l'échelle et valeurs dessinés par les données | | Fiche d'estimation | Portrait de la propriété | | **Vrai-Prix Maps** — votre quartier, estimé (grappes de valeurs 2026) | **Ka · agent IA** — évaluation en conversant, calcul montré | | Recherche par carte | Agent IA Ka | | **Statistiques provinciales** — 2,01 billions $, palmarès des municipalités | **Pages municipalités** — 1 097 pages programmatiques indexables | | Statistiques provinciales | Page municipalité Gatineau | | **Méthodologie publique** — 7 sections, normes IAAO, rien de caché | **Mobile** — même expérience, menu plein écran pur CSS | | Méthodologie | Accueil mobile Fiche mobile | ## Les chiffres qui comptent Validation sur **102 943 ventes jamais vues** du modèle (holdout aléatoire) et **test temporel strict** (entraîné sans 2026, testé sur **68 364 ventes de 2026**), selon la **norme IAAO sur études de ratios** : | Métrique | Holdout | Test temporel 2026 | Cible IAAO | |---|---|---|---| | Erreur médiane (MdAPE) | **11,0 %** | 14,0 % | — | | À ±20 % du prix réel | **72,1 %** | 66,7 % | — | | Ratio médian | **0,995** | 0,961 | 0,90–1,10 ✅ | | COD | 22,7 (condos : **11,9** ✅) | 23,7 | ≤ 15–20 | | PRD | 1,092 | 1,075 | 0,98–1,03 | | R² (log) | **0,789** | 0,743 | — | ## Architecture ``` ┌─ Données ouvertes MAMH ──────────────────────────────────────────┐ │ Rôles d'évaluation foncière géoréférencés 2021-2026 │ │ 6 millésimes × ~3,7 M unités = 22 150 285 observations │ └──────────────┬───────────────────────────────────────────────────┘ │ fusion spatiale XY (EPSG:32198) │ kNN k=25 ≤200 m + jointure par valeur ≤500 m │ → 99,88 % appariées, distance médiane 0,6 m ┌──────────────▼───────────────┐ ┌──────────────────────────────┐ │ 745 119 transactions │ │ Modèle hédonique LightGBM │ │ enrichies du rôle ├──▶│ log(prix), 18 variables │ └──────────────────────────────┘ │ + quantiles P10/P90 │ │ → 22 M d'estimations │ └──────────────┬───────────────┘ ┌─ Application Next.js 16 ─────────────────────────▼───────────────┐ │ SQLite (units + FTS5 + transactions + indice de marché) │ │ Moteur comparables (ajust. marché/superficie/âge, médiane │ │ pondérée) · Ka Maps (Mapbox GL) · agent Ka (Claude + 12 outils) │ │ RadarMap SVG · MetricViz · rapports pdfkit · SSO KA ID │ │ SEO programmatique (1 097 municipalités · sitemaps 3,76 M URLs) │ └──────────────────────────────────────────────────────────────────┘ ``` ### Stack - **Frontend / API** : **Next.js 16** (App Router), **TypeScript strict**, **Tailwind CSS 4** - **Base** : **SQLite** (better-sqlite3) — **3,75 M unités**, **FTS5**, index spatiaux *(non incluse dans ce dépôt)* - **Cartes** : **`@groupe-ka/ka-maps`** (framework carto partagé du Groupe KA, moteur **Mapbox GL**, style Standard 3D) - **Agent IA** : **SDK Anthropic** (Claude Haiku 4.5, streaming SSE, boucle agentique manuelle, prompt caching) - **Rapports** : **pdfkit**, 100 % vectoriels, polices embarquées (Space Grotesk / Inter / JetBrains Mono) - **Modèle** (pipeline externe) : **LightGBM**, scikit-learn, pandas, pyogrio/GDAL - **Tests** : **Vitest** (moteur d'estimation) + **Playwright** (mobile, captures d'écran) - **Prod** : **PM2** + **ngrok** sur le nœud **M3U96a** du cluster MacLustr ## Le moteur d'estimation (`src/lib/engine.ts`) 1. **Comparables** : ventes candidates dans un rayon adaptatif (**2,2 → 28 km**), filtrées par type et superficie (±20 → ±40 %) ; 2. **Ajustements affichés en dollars** : conditions de marché (indice mensuel $/m² lissé 3 mois), superficie (50 % du $/m² du comparable, plafonné ±25 %), âge (0,5 %/an, plafonné ±10 %) ; 3. **Pondération gaussienne** : distance (σ 1 500 m), récence (σ 24 mois), similarité de superficie (σ 25 %) ; 4. **Fusion** : **65 % modèle hédonique** pré-calculé + **35 % médiane pondérée des comparables** ; 5. **Confiance** : nombre de comparables, dispersion, largeur de fourchette → **A/B/C/D**. Chaque fiche montre le calcul **en entier** : les comparables retenus, chaque ajustement, les poids, la réconciliation. Le rapport PDF « professionnel » reprend la grille d'ajustement au format des évaluateurs (avec les encadrés « ceci n'est pas un rapport OEAQ »). ## Ka — l'agent IA Page [`/ka`](https://www.vrai-prix.com/ka) : un agent conversationnel branché **en direct** sur le registre et le moteur. Boucle agentique manuelle (**12 tours max**) avec **12 outils serveur** : `chercher_propriete` (FTS avec relaxation progressive), `evaluer_propriete`, `dossier_complet` (évaluation + marché + tendance + comparables + synthèse en un appel), `comparables_detailles`, `indice_marche`, `stats_municipalite`, `stats_provinciales`, `evaluer_parc`, `comparer_proprietes`, `estimation_manuelle`, `chercher_proprietes_secteur`, `liens_rapports` (réfs persistantes anti-hallucination pour les PDF). **Streaming SSE**, tableaux HTML dans le fil, clarification quand l'adresse est ambiguë. ## SEO programmatique — 3,76 M d'URLs indexables Tout le contenu est servi **en HTML complet dès la première requête** (SSR), avec un maillage interne crawlable : - **`/municipalites`** → index A-Z des **1 097 municipalités** ; - **`/municipalite/[slug]`** (**1 097 pages**) → médiane 2026, valeur totale, croissance 2021→2026, médianes par type, 24 fiches liées, municipalités voisines ; - **`/municipalite/[slug]/[type]`** (**~5 900 pages**) → `maison`, `plex`, `chalet`, `condo`, `terrain`, `maison-mobile`, `autre` ; - **`/estimation/[id]`** (**3,75 M fiches**) → title et meta description **uniques par fiche** (adresse + estimation), canonical, JSON-LD `SingleFamilyResidence`/`Residence`/`Place` + `BreadcrumbList` ; - **Sitemaps** : `/sitemap-index.xml` → `/sitemap.xml` (7 043 pages) + `/estimation/sitemap/0-83.xml` (45 000 fiches chacun, générés à la volée depuis SQLite par plages de rowid) ; - JSON-LD `Organization` + `WebSite` global, `robots.txt` propre, `noindex` sur les pages privées/paramétriques. Les agrégats par municipalité (`src/lib/municipalites.ts`) sont calculés en SQL et mis en cache mémoire par processus — première visite ~1 s (Montréal), ensuite ~30 ms. ## Structure du projet ``` src/ ├─ app/ │ ├─ page.tsx / HomeClient.tsx # accueil (recherche + estimation manuelle) │ ├─ estimation/[id]/ # fiche (SSR + generateMetadata + JSON-LD) │ ├─ estimation/manuelle/ # estimation sans adresse (noindex) │ ├─ municipalites/ # index A-Z (programmatique) │ ├─ municipalite/[slug]/[type]/ # 1 097 + ~5 900 pages programmatiques │ ├─ carte/ # Vrai-Prix Maps (Ka Maps / Mapbox GL) │ ├─ ka/ # agent IA (streaming SSE) │ ├─ parc/ # parc immobilier multi-adresses │ ├─ stats/ # statistiques provinciales │ ├─ compte/ # carte de membre Groupe KA (SSO) │ ├─ methodologie|conditions|confidentialite/ │ ├─ sitemap.ts · estimation/sitemap.ts · sitemap-index.xml/ · robots.ts │ └─ api/ # endpoints (voir tableau) ├─ components/ # SearchBox, ResultView, MetricViz, RadarMap, │ # KaCarte, KaChat, StatsView, LangContext… ├─ lib/ │ ├─ engine.ts # moteur comparables (testé Vitest) │ ├─ estimator.ts # service DB ↔ moteur │ ├─ db.ts # SQLite (better-sqlite3, WAL) │ ├─ municipalites.ts # agrégats programmatiques (cache mémoire) │ ├─ seo.ts # slugs, libellés, constantes SEO │ ├─ ka/ # agent IA (outils serveur, prompt) │ ├─ ka-auth.ts · hub-profile.ts # SSO KA ID (JWT HS256, node:crypto pur) │ └─ report*.ts # 4 générateurs PDF (pdfkit) └─ data/stats.json # agrégats provinciaux précalculés ``` ## Endpoints | Route | Description | |---|---| | `GET /api/search?q=` | Autocomplétion d'adresses (FTS5) | | `GET /api/estimate?id=` | Estimation complète d'une unité | | `POST /api/estimate` | Estimation manuelle (municipalité + caractéristiques) | | `GET /api/nearby?lat&lng&halfLat&halfLng` | Unités dans une boîte englobante (carte) | | `POST /api/ka` | Agent IA (streaming SSE, boucle d'outils) | | `POST /api/portfolio` | Agrégation d'un parc (jusqu'à 40 ids) | | `GET /api/report?id=` | Rapport PDF standard (3 pages) | | `GET /api/report/pro?id=` | Rapport détaillé format professionnel (6 pages) | | `GET /api/report/portfolio?ids=` | Rapport de parc consolidé | | `GET /api/report/stats` | Rapport statistique provincial | | `GET /api/stats` · `/api/stats/dashboard` | Agrégats provinciaux (métriques live du hub Groupe KA) | | `GET /api/auth/ka/login` · `/callback` | SSO KA ID (hub groupe-ka.com) | | `GET /api/auth/me` · `POST /api/auth/logout` | Session (cookie signé, sans table users) | ## Démarrage local ```bash npm install # placer la base de données dans data/vraiprix.db (non versionnée) npm run dev # développement npm run test # tests du moteur (Vitest) npm run build && npm run start ``` ### Variables d'environnement | Variable | Rôle | |---|---| | `VRAIPRIX_DB` | Chemin de la base SQLite (défaut : `data/vraiprix.db`) | | `NEXT_PUBLIC_SITE_URL` | URL canonique (défaut : `https://www.vrai-prix.com`) | | `ANTHROPIC_API_KEY` | Agent IA Ka (page `/ka`) | | `NEXT_PUBLIC_MAPBOX_TOKEN` | Vrai-Prix Maps (page `/carte`) | | `KA_SSO_SECRET` · `KA_AUTH_SECRET` · `KA_HUB_URL` · `KA_BASE_URL` | SSO KA ID | > **Note données** : la base SQLite (1,5 Go) et les pipelines d'entraînement ne sont pas versionnés. Données sources : [rôles d'évaluation foncière du Québec](https://www.donneesquebec.ca/recherche/dataset/roles-d-evaluation-fonciere-du-quebec) (MAMH, licence ouverte). Les renseignements des propriétaires sont caviardés à la source. > **Captures d'écran** : `node scripts/screenshots.mjs` les régénère depuis la prod (Playwright). ## Déploiement L'app tourne en production sur le cluster **MacLustr** : | | | |---|---| | **Nœud** | **M3U96a** (Mac Studio M3 Ultra, 32 cœurs / 96 Go) — `~/apps/vrai-prix` | | **Port** | **8090** (`next start -H 0.0.0.0 -p 8090`) | | **Domaine** | **[www.vrai-prix.com](https://www.vrai-prix.com)** (tunnel ngrok) | | **Processus PM2** | **`vrai-prix`** (app Next.js) + **`vrai-prix-ngrok`** (tunnel) | ```bash # sur M3U96a, après un changement : cd ~/apps/vrai-prix npm run build pm2 restart vrai-prix pm2 logs vrai-prix --lines 30 # vérifier le démarrage ``` ## Développement remote-first ⚠️ **La source de vérité est le repo git sur le nœud M3U96a** (`~/apps/vrai-prix`), **pas** une copie laptop. Toute modification se fait sur le nœud via SSH : édition, `npm run build`, `pm2 restart vrai-prix`, puis commit + push **sur le nœud**. - Remote **`origin` = spbgit** (git perso, git.spboucher.ai) — sur **M3U96a** le remote pointe directement sur le chemin local **`/Users/simon-pierreboucher/srv/git/vrai-prix.git`** (bare repo) ; - Remote secondaire `github` → `github.com/spboucher-ai/vrai-prix` ; - Référence complète du workflow : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`. ```bash ssh M3U96a cd ~/apps/vrai-prix # … modifier, builder, redémarrer … git add && git commit -m "…" && git push origin main ``` ## Design Identité « éditorial sharp » : papier grainé `#f5f3ee`, encre `#141814`, **accent rouge vif `#ff5148`**, bordeaux `#9e2a25` — bordures encre 1,5 px, ombres décalées, display uppercase (Space Grotesk), micro-étiquettes mono (JetBrains Mono), ticker marquee, visualisations SVG data-driven (façade du bâtiment, terrain à l'échelle, jauge de confiance, sparklines) — **zéro image bitmap dans l'app**. ## Auteur **Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai) *Estimations statistiques à titre indicatif — ne remplacent pas une évaluation par un évaluateur agréé (OEAQ). Voir [conditions](https://www.vrai-prix.com/conditions) et [méthodologie complète](https://www.vrai-prix.com/methodologie).* ---
Un service **Groupe Ka**