SPB Git

spb/valoplex

Public

ValoPlex — moteur d'évaluation spécialisé pour les plex au Québec, petit frère de Vrai-Prix.

feat: ValoPlex — moteur d'évaluation spécialisé plex du Québec

393 867 plex (2+ logements), modèle hédonique en ratio prix/rôle avec
calibration conforme Mondrian par gabarit, ajustement par porte, pro forma
d'investisseur complet (hypothèque canadienne, projection 5 ans, point mort,
droits de mutation), rapports PDF. Identité orange #ff9f45.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 9, 2026) Browse files

68 changed files +16,147 −0

68 changed files
added .gitignore +17 −0
@@ -0,0 +1,17 @@
1 +# dépendances / build
2 +app/node_modules/
3 +app/.next/
4 +*.tsbuildinfo
5 +app/next-env.d.ts
6 +
7 +# données — jamais versionnées (base SQLite, modèles, estimations)
8 +app/data/
9 +data/
10 +models/
11 +
12 +# divers
13 +.DS_Store
14 +*.log
15 +app/test-results/
16 +app/playwright-report/
17 +__pycache__/
added README.md +108 −0
@@ -0,0 +1,108 @@
1 +# ValoPlex
2 +
3 +> **La vraie valeur de votre plex, porte par porte.**
4 +> Le moteur d'évaluation spécialisé plex du Québec — 393 867 immeubles de 2 logements et plus, 1 733 744 portes, un modèle hédonique en ratio à l'épreuve des échelles.
5 +
6 +![Plex](https://img.shields.io/badge/plex-393%C2%A0867-ff9f45?style=flat-square)
7 +![Portes](https://img.shields.io/badge/portes-1%C2%A0733%C2%A0744-ff9f45?style=flat-square)
8 +![Ventes](https://img.shields.io/badge/ventes_de_plex-91%C2%A0060-ff9f45?style=flat-square)
9 +![MdAPE](https://img.shields.io/badge/MdAPE-13,8%25-b25f16?style=flat-square)
10 +![Couverture](https://img.shields.io/badge/fourchette_calibr%C3%A9e-80,0%25-b25f16?style=flat-square)
11 +![Valeur totale](https://img.shields.io/badge/valeur_totale_plex_QC-400,6%C2%A0G%24-b25f16?style=flat-square)
12 +![Tests](https://img.shields.io/badge/tests-33%2F33-141814?style=flat-square)
13 +![Next.js](https://img.shields.io/badge/Next.js-16-141814?style=flat-square&logo=nextdotjs)
14 +![TypeScript](https://img.shields.io/badge/TypeScript-strict-141814?style=flat-square&logo=typescript)
15 +
16 +**En production : [www.valoplex.com](https://www.valoplex.com)** · petit frère spécialisé de [Vrai-Prix](https://www.vrai-prix.com)
17 +
18 +---
19 +
20 +## Pourquoi un moteur spécialisé plex ?
21 +
22 +Un plex est un **immeuble à revenus** : sa valeur se pense par porte, par revenu, par TGA — pas comme une unifamiliale. ValoPlex couvre **tous** les immeubles résidentiels multi-logements du Québec (CUBF 1000, 2 logements et plus — du duplex à la tour) avec un modèle et une interface dédiés à cette logique.
23 +
24 +| Fonctionnalité | Description |
25 +|---|---|
26 +| 🚪 Économie par porte | Valeur/porte, rôle/porte, aire/porte, terrain/porte + benchmark vs marché local |
27 +| 🧮 Pro forma investisseur | Loyer implicite, état des résultats complet, hypothèque canadienne, DSCR, cashflow/porte, projection 5 ans, point mort, sensibilité aux taux — interactif |
28 +| 💸 Frais « tout le kit » | Droits de mutation (barème QC + barème bonifié de Montréal), notaire, inspection, liquidités totales |
29 +| 🏢 Frise des portes | Une porte SVG dessinée par logement (signature) + badge DUPLEX/TRIPLEX/…/MULTI N PORTES |
30 +| 📍 Adresses officielles | Toutes les inscriptions d'adresses de l'unité (table des adresses du rôle) |
31 +| ⚖️ Ajustement portes | 4ᵉ ajustement des comparables : 50 % du prix/porte, plafonné ±30 % |
32 +| 🗺️ Plan des comparables | Carte plan d'arpenteur SVG (azimut + distance réels), état vide honnête si aucun plex comparable |
33 +| 📄 Rapports PDF | Standard 3 p. (avec pro forma), professionnel bancaire 6 p., parc, statistique provincial |
34 +| 📉 Statistiques | Les plex du Québec valent **400,6 G$** — répartition par gabarit et par municipalité |
35 +
36 +## Le modèle : hédonique en ratio, calibré par gabarit
37 +
38 +Trois idées font la différence :
39 +
40 +1. **Cible en ratio `log(prix / valeur au rôle)`** — pas le prix. Un modèle en prix brut, dominé par la masse des duplex, écrase les grands immeubles (nous avons mesuré une tour au rôle de 705 M$ prédite… 5,6 M$). En ratio, le duplex de 400 k$ et la tour de 700 M$ sont évalués avec la même justesse relative. Le modèle connaît en plus **l'ancienneté du rôle** (mois depuis la date des conditions du marché du dénominateur).
41 +2. **Rétrécissement hors domaine** : au-delà de 12 portes (données de ventes rares), l'estimation glisse vers l'ancre empirique de sa bande de gabarit (poids du modèle : 55 % → 20 %).
42 +3. **Calibration conforme Mondrian** : les fourchettes P10–P90 sont calibrées **par bande de portes** sur un ensemble de calibration dédié — couverture vérifiée de **80,0 %** (pas promise : mesurée).
43 +
44 +### Validation (norme IAAO, 13 617 ventes de test jamais vues)
45 +
46 +| Segment | MdAPE | Ratio médian |
47 +|---|---|---|
48 +| Tous les plex | **13,8 %** | 0,976 |
49 +| Duplex (2 portes) | 13,9 % | 0,970 |
50 +| Triplex (3 portes) | **12,6 %** | 0,983 |
51 +| 4–5 portes | 14,1 % | 0,971 |
52 +| 6–12 portes | 17,9 % | 0,955 |
53 +| Test temporel 2026 (9 172 ventes) | 13,8 % | 0,972 |
54 +| Couverture de la fourchette (calibrée) | **79,9 ≈ 80 %** | — |
55 +
56 +## Le pro forma (`app/src/lib/proforma.ts` — 20 tests)
57 +
58 +Approche revenu **inversée** : quelle structure de revenus la valeur estimée suppose-t-elle ?
59 +
60 +- Loyer implicite en forme fermée : `RB = (V·TGA + F) / (1 − vacance − %variables)`
61 +- Dépenses détaillées : taxes municipales et scolaire (dérivées du rôle), assurances, entretien, gestion, déneigement, énergie des communs, réserve
62 +- Hypothèque **canadienne** (composition semestrielle), cashflow/porte/mois, DSCR, cash-on-cash
63 +- Capital remboursé an 1, **projection 5 ans** (équité, gain total, multiple sur liquidités)
64 +- **Loyer de point mort** et **taux de point mort** (bissection), sensibilité taux ±1 %
65 +- Droits de mutation : barème provincial + barème bonifié de Montréal (jusqu'à 4 %)
66 +
67 +Cinq curseurs (taux, mise de fonds, amortissement, TGA, appréciation) — tout recalcule en direct.
68 +
69 +## Structure du dépôt
70 +
71 +```
72 +valoplex/
73 +├── app/ # application Next.js 16 (TypeScript strict, Tailwind 4)
74 +│ ├── src/lib/ # engine.ts (comparables), proforma.ts, report*.ts (pdfkit)
75 +│ ├── src/components/ # RadarMap, MetricViz, Proforma, DoorsRow…
76 +│ └── src/app/ # pages + API routes
77 +└── scripts/ # pipeline données/modèle (Python)
78 + ├── hedonic_plex.py # entraînement (ratio + Mondrian) et prédictions 2021-2026
79 + └── build_valoplex_db.py # base SQLite + stats provinciales
80 +```
81 +
82 +## Démarrage
83 +
84 +```bash
85 +cd app && npm install
86 +# placer la base dans app/data/valoplex.db (non versionnée)
87 +npm run dev
88 +npm run test # 33 tests (moteur + finance)
89 +```
90 +
91 +Pipeline (nécessite les rôles MAMH et les transactions enrichies, non versionnés) :
92 +
93 +```bash
94 +python3 scripts/hedonic_plex.py train && python3 scripts/hedonic_plex.py predict
95 +python3 scripts/build_valoplex_db.py
96 +```
97 +
98 +> **Note données** : base SQLite (165 Mo), modèles entraînés et parquets d'estimation non 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).
99 +
100 +### Design
101 +
102 +Identité « éditorial sharp » : papier grainé `#f5f3ee`, encre `#141814`, **accent orange vif `#ff9f45`**, ocre brûlé `#b25f16` — sœur chromatique de Vrai-Prix (rouge).
103 +
104 +## Auteur
105 +
106 +**Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai)
107 +
108 +*Estimations statistiques à titre indicatif — ne remplacent pas une évaluation par un évaluateur agréé (OEAQ) ni un conseil financier. Voir [conditions](https://www.valoplex.com/conditions) et [méthodologie](https://www.valoplex.com/methodologie).*
added app/.gitignore +41 −0
@@ -0,0 +1,41 @@
1 +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 +
3 +# dependencies
4 +/node_modules
5 +/.pnp
6 +.pnp.*
7 +.yarn/*
8 +!.yarn/patches
9 +!.yarn/plugins
10 +!.yarn/releases
11 +!.yarn/versions
12 +
13 +# testing
14 +/coverage
15 +
16 +# next.js
17 +/.next/
18 +/out/
19 +
20 +# production
21 +/build
22 +
23 +# misc
24 +.DS_Store
25 +*.pem
26 +
27 +# debug
28 +npm-debug.log*
29 +yarn-debug.log*
30 +yarn-error.log*
31 +.pnpm-debug.log*
32 +
33 +# env files (can opt-in for committing if needed)
34 +.env*
35 +
36 +# vercel
37 +.vercel
38 +
39 +# typescript
40 +*.tsbuildinfo
41 +next-env.d.ts
added app/AGENTS.md +9 −0
@@ -0,0 +1,9 @@
1 +<!-- BEGIN:nextjs-agent-rules -->
2 +
3 +# This is NOT the Next.js you know
4 +
5 +This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in `node_modules/next/dist/docs/` (resolved from this file's directory; in monorepos the `next` package may not be visible from the repo root) before writing any code. Heed deprecation notices.
6 +
7 +This block is written and re-added by `next dev` — verify at `node_modules/next/dist/server/lib/generate-agent-files.js`. Removing it from a diff only re-creates the uncommitted change; committing it with your work keeps the tree clean.
8 +
9 +<!-- END:nextjs-agent-rules -->
added app/CLAUDE.md +1 −0
@@ -0,0 +1 @@
1 +@AGENTS.md
added app/README.md +3 −0
@@ -0,0 +1,3 @@
1 +# ValoPlex — application
2 +
3 +Voir le [README principal](../README.md) à la racine du dépôt.
added app/assets/fonts/Inter-Regular.ttf +0 −0

Binary file not shown.

added app/assets/fonts/JetBrainsMono-Bold.ttf +0 −0

Binary file not shown.

added app/assets/fonts/JetBrainsMono-Regular.ttf +0 −0

Binary file not shown.

added app/assets/fonts/SpaceGrotesk-Bold.ttf +0 −0

Binary file not shown.

added app/assets/fonts/SpaceGrotesk-Medium.ttf +0 −0

Binary file not shown.

added app/eslint.config.mjs +18 −0
@@ -0,0 +1,18 @@
1 +import { defineConfig, globalIgnores } from "eslint/config";
2 +import nextVitals from "eslint-config-next/core-web-vitals";
3 +import nextTs from "eslint-config-next/typescript";
4 +
5 +const eslintConfig = defineConfig([
6 + ...nextVitals,
7 + ...nextTs,
8 + // Override default ignores of eslint-config-next.
9 + globalIgnores([
10 + // Default ignores of eslint-config-next:
11 + ".next/**",
12 + "out/**",
13 + "build/**",
14 + "next-env.d.ts",
15 + ]),
16 +]);
17 +
18 +export default eslintConfig;
added app/next.config.ts +8 −0
@@ -0,0 +1,8 @@
1 +// Auteur : Simon-Pierre Boucher — contact@spboucher.ai
2 +import type { NextConfig } from "next";
3 +
4 +const nextConfig: NextConfig = {
5 + serverExternalPackages: ["better-sqlite3", "pdfkit"],
6 +};
7 +
8 +export default nextConfig;
added app/package-lock.json +8233 −0
Large diff (8,233 lines) is not loaded by default to keep the page fast.Load diff
added app/package.json +33 −0
Large diff (33 lines) is not loaded by default to keep the page fast.Load diff
added app/postcss.config.mjs +7 −0
Large diff (7 lines) is not loaded by default to keep the page fast.Load diff
added app/public/file.svg +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added app/public/globe.svg +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added app/public/next.svg +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added app/public/vercel.svg +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added app/public/window.svg +1 −0
Large diff (2 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/estimate/route.ts +42 −0
Large diff (42 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/lead/route.ts +17 −0
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/portfolio/route.ts +14 −0
Large diff (14 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/report/portfolio/route.ts +25 −0
Large diff (25 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/report/pro/route.ts +28 −0
Large diff (28 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/report/route.ts +26 −0
Large diff (26 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/report/stats/route.ts +17 −0
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/api/search/route.ts +20 −0
Large diff (20 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/conditions/page.tsx +119 −0
Large diff (119 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/confidentialite/page.tsx +129 −0
Large diff (129 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/estimation/[id]/page.tsx +17 −0
Large diff (17 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/estimation/manuelle/page.tsx +36 −0
Large diff (36 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/favicon.ico +0 −0

Binary file not shown.

added app/src/app/globals.css +234 −0
Large diff (234 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/layout.tsx +94 −0
Large diff (94 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/methodologie/page.tsx +259 −0
Large diff (259 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/page.tsx +100 −0
Large diff (100 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/parc/page.tsx +358 −0
Large diff (358 lines) is not loaded by default to keep the page fast.Load diff
added app/src/app/stats/page.tsx +13 −0
Large diff (13 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/CookieConsent.tsx +226 −0
Large diff (226 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/EstimateForm.tsx +95 −0
Large diff (95 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/FooterNav.tsx +34 −0
Large diff (34 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/HeaderNav.tsx +153 −0
Large diff (153 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/LangContext.tsx +46 −0
Large diff (46 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/LeadForm.tsx +56 −0
Large diff (56 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/LegalPage.tsx +78 −0
Large diff (78 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/MetricViz.tsx +470 −0
Large diff (470 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/Proforma.tsx +212 −0
Large diff (212 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/RadarMap.tsx +252 −0
Large diff (252 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/ResultView.tsx +595 −0
Large diff (595 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/SearchBox.tsx +95 −0
Large diff (95 lines) is not loaded by default to keep the page fast.Load diff
added app/src/components/StatsView.tsx +282 −0
Large diff (282 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/db.ts +144 −0
Large diff (144 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/engine.test.ts +155 −0
Large diff (155 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/engine.ts +312 −0
Large diff (312 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/estimator.ts +304 −0
Large diff (304 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/i18n.ts +117 −0
Large diff (117 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/proforma.test.ts +123 −0
Large diff (123 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/proforma.ts +290 −0
Large diff (290 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/report-portfolio.ts +308 −0
Large diff (308 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/report-pro.ts +448 −0
Large diff (448 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/report-stats.ts +206 −0
Large diff (206 lines) is not loaded by default to keep the page fast.Load diff
added app/src/lib/report.ts +454 −0
Large diff (454 lines) is not loaded by default to keep the page fast.Load diff
added app/tsconfig.json +34 −0
Large diff (34 lines) is not loaded by default to keep the page fast.Load diff
added app/vitest.config.ts +9 −0
Large diff (9 lines) is not loaded by default to keep the page fast.Load diff
added scripts/build_valoplex_db.py +255 −0
Large diff (255 lines) is not loaded by default to keep the page fast.Load diff
added scripts/hedonic_plex.py +363 −0
Large diff (364 lines) is not loaded by default to keep the page fast.Load diff
Menu

Theme