Framework de juste valeur (fair value) — Lou-Ka / Groupe-KA
Module : louka/fairvalue.py · Version modèle : fv-logement-1.0 · Créé : 2026-08-18
Estime en continu la juste valeur locative de chaque annonce publiée et la
classe sous le marché / dans le marché / au-dessus du marché. Conçu comme le
moteur générique « estimation de juste valeur » du Groupe-KA : toutes les
spécificités logement vivent dans CONFIG (voir « Décliner sur immo-ka /
auto-ka » plus bas).
1. Données d'entrée
- Jeu de référence : toutes les annonces
active=1 AND published=1 AND dup_of IS NULLau prix mensuel plausible (175–20 000 $). Les annonces en quarantaine qualité (quality.py) sont exclues — le framework repose sur les données enrichies et nettoyées des connecteurs. - Exclusions supplémentaires : prix affichés non mensuels (« /semaine »,
« par nuit » — regex
_NON_MENSUEL) ; elles ne polluent ni les médianes ni les verdicts. - Chambres/colocations : segment dédié
bed="ch"(détectionunit_type="Chambre"ou regex_CHAMBREsur le titre) — une chambre n'est jamais comparée au loyer d'un logement entier. - Rafraîchi à chaque cycle de synchronisation (
ingest.watch, après géocodage, quality et dedup) et à la demande :python run.py fairvalue. Calcul complet : ~15 s pour ~47 000 annonces (pur Python + grille spatiale).
2. Méthode en trois niveaux
N1 — Référence par segment
Segments en échelle de repli, avec volume minimal min_segment=8 :
ville|quartier|chambres → ville|chambres → chambres (province) → global.
Statistiques robustes : médiane + p25/p75 (jamais la moyenne).
Groupes de chambres : 0 (studio), 1, 2, 3, 4+, ch (chambre/coloc).
N2 — Ajustements par caractéristiques
Coefficients multiplicatifs appris des données (aucun coefficient inventé) :
médiane de prix/p50(segment) des annonces AVEC la caractéristique ÷ celle des
annonces SANS (minimum coef_min_pairs=60 de chaque côté, bornés à
coef_bounds=(0.85, 1.35)). Centrage obligatoire : le p50 du segment
contient déjà le mélange avec/sans, donc l'ajustement appliqué est
(c si présent, sinon 1) / (part×c + (1−part)) — sans ce centrage, toutes les
estimations gonflent et tout paraît « sous-évalué » (bug observé lors de la
première passe : 54 % de « sous »). Caractéristique au statut inconnu = effet
moyen (neutre). Superficie : (aire/aire_type_segment)^0.35, borné.
Coefficients appris le 2026-08-18 : stationnement ×1,27 · lave-vaisselle ×1,13 · électricité incluse ×1,12 · meublé ×1,11 · chauffage inclus ×1,10 · balcon ×1,08 · climatisation ×1,07 · ascenseur ×1,06 · piscine ×1,06.
N3 — Comparables kNN géographiques
Pour les annonces géolocalisées : grille spatiale ~2 km, voisines du même
groupe de chambres à < 3 km, pondération gaussienne exp(−(d/800 m)²) avec
bonus ×1,3 si même type d'unité, k=12, minimum 4 comparables. Estimateur :
médiane pondérée des loyers (la moyenne serait tirée par l'asymétrie).
Combinaison et garde-fous
fv = 0,6×kNN + 0,4×(segment ajusté)quand le kNN existe, sinon segment ajusté.- Couloir de sécurité :
fvbornée à[0,70×p25 ; 1,30×p75]du segment. - Fourchette : dispersion relative du segment appliquée à
fv; élargie si les deux méthodes divergent de > 25 % (et la confiance est abaissée). - Confiance :
fort(≥ 8 comparables, segment fin, méthodes concordantes),moyen(≥ 4 comparables ou segment ville ≥ 15 annonces),faiblesinon.
3. Classification et règles de prudence
Écart relatif d = (prix − fv)/fv :
sous si d ≤ −8 %, sur si d ≥ +8 %, marche entre les deux.
Pas de verdict (pastille absente, estimation « indicative ») si :
- confiance
faible(peu de comparables / segment rare) ; - prix ambigu (
price_from: « à partir de » = prix plancher) ; - écart hors du plausible (
verdict_dev_bounds = (−50 %, +100 %)) — donnée probablement corrompue (coquille de prix, périodicité mal lue).
4. Stockage, historisation, traçabilité
fairvalue(1 rangée/annonce) : fv, fourchette, écart, verdict, confiance, méthode utilisée, nb de comparables, segment de référence, version du modèle, horodatage — chaque estimation est explicable.fv_segments(append-only) : p25/p50/p75 + volume de chaque segment (niveaux 1-2) à chaque calcul → évolution des loyers de référence dans le temps.- Cache miniatures/geo non touchés.
fairvalue.explain(uid)reconstruit la distribution du segment pour la fiche (histogramme 24 classes, bornes p1-p99).
5. Exposition
- Site : pastille sur les cartes (
FairValueBadge, icône + libellé + %, lisible daltoniens), bloc « Analyse de prix Lou-Ka » sur la fiche (PriceAnalysis: fv, fourchette, écart, confiance, mini-histogramme avec marqueurs « Ce loyer » / « Juste valeur »), filtre « ▼ Sous le marché » (tri meilleures affaires d'abord :?deal=sous&sort=deal), page de transparence/juste-valeur. - API Lou-Ka : champs
fv, fv_low, fv_high, fv_deviation, fv_verdict, fv_confidencedans/api/listings(+ filtredeal=, trisort=deal), pins GeoJSON,/api/fairvalue/{uid}(analyse détaillée + histogramme). - API-KA :
/api/v1/louka/fairvalue/{uid}(proxy live pour l'app iOS KA) ; les snapshots quotidienslouka_dataembarquent automatiquement les champs fv. - Stats : KPI « Annonces sous le marché », donut de répartition, tableau
« Écart au marché par ville » (
statsdash.py) — repris dans le PDF Groupe-KA.
6. Calibration et recalibrage
- Seuils initiaux ±8 % : distribution obtenue ≈ 24 % sous / 39 % dans / 24 % sur / 13 % sans verdict (2026-08-18, ~47 k annonces).
- Procédure de recalibrage (périodique, trimestrielle suggérée) :
SELECT fv_verdict, durée de présence— comparer le temps en ligne (last_seen − first_seendes annonces désactivées) par verdict : les « sous » doivent partir significativement plus vite que les « sur ».- Ajuster
seuil_sous/seuil_surpour maximiser cette séparation. - Re-vérifier le centrage :
AVG(deviation)sur le parc doit rester ≈ 0 (±2 %) ; sinon revoir les coefficients (biais N2) ou le poids kNN. - Bumper
MODEL_VERSIONà tout changement (traçabilité des estimations).
- Tests de non-régression :
tests/test_fairvalue.py(synthétiques, hors-ligne).
7. Décliner sur immo-ka (vente) et auto-ka (véhicules)
Le moteur est générique : segments en échelle + coefficients centrés + kNN + couloir + confiance. Pour décliner :
- Copier
fairvalue.py, remplacer_load()(requête du jeu de référence) etCONFIG:- immo-ka : segment =
ville|quartier|type de propriété|chambres, valeur = prix demandé, caractéristiques = garage, sous-sol fini, piscine, année, terrain ; ajustement superficie avec élasticité plus forte (~0,6) ; kNN rayon 2 km. Bornes de plausibilité : 50 k–10 M $. - auto-ka : segment =
marque|modèle|année±1, « géographie » remplacée par la distance en kilométrage (kNN sur km au lieu de lat/lng), caractéristiques = transmission, motorisation, groupe d'options ; dépréciation = équivalent de l'ajustement superficie.
- immo-ka : segment =
- Garder tels quels :
_pct, centrage des coefficients, couloir, médiane pondérée kNN, règles de prudence, historisation,MODEL_VERSION. - Reprendre l'UI :
FairValueBadge/PriceAnalysissont paramétrés par les mêmes champs API (fv_*).
8. Limites connues
- Les « bonnes affaires » à −40/−50 % contiennent encore quelques loyers à la
chambre non détectés (annonces ambiguës « chambre dans grand 4½ » sans
mots-clés) — bornées par
verdict_dev_bounds. - Les alertes « bonne affaire » sur critères sauvegardés nécessitent une
infrastructure de notification (courriel/push) absente de Lou-Ka à ce jour ;
l'URL
/?deal=sousest partageable/enregistrable en attendant. - Segments ruraux minces → repli
ville|chambresprovincial, confiancefaible, pas de pastille (comportement voulu : rien d'inventé).