fix(ai): realistic pre-market rent recommendation — annual units + anchor on current price

- getMarketRent now returns CHF/m²/YEAR (stored medians are monthly → ×12); fixes annual-vs-monthly mismatch that also affected MarketPricePanel and NegotiationInsightsPanel
- recommendPreMarketRent: anchor on the unit's current rent and adjust within a bounded ±15% by market momentum (vacancy=supply, demand strength, days-on-market, trend) instead of jumping to the city-wide (prime-skewed) median — no more "double the price" suggestions; median only used as a headroom check
- tests updated to annual market values

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-06-21 00:39:37 +02:00
parent fb029cf0bc
commit 09095a1a32
3 changed files with 37 additions and 32 deletions
+9 -7
View File
@@ -6,9 +6,9 @@ export interface CityIntelligence {
purchasingPowerIndex: number // Kaufkraft-Index (CH = 100)
dominantIndustryClusters: string[]
plannedInfrastructure: { project: string; timeline: string; impact: string }[]
medianRentOffice: number // CHF/m² für Bürofläche
medianRentLogistics: number
medianRentRetail: number
medianRentOffice: number // CHF/m²/Monat (Bürofläche) — getMarketRent rechnet auf Jahr um
medianRentLogistics: number // CHF/m²/Monat
medianRentRetail: number // CHF/m²/Monat
avgDaysOnMarket: number // Durchschnittliche Tage bis Vermietung
demandStrength: 'LOW' | 'MEDIUM' | 'HIGH' | 'VERY_HIGH'
taxIndexCanton: number // Steuerindex 100 = CH-Mittel
@@ -137,13 +137,15 @@ export function getCityIntelligence(city: string): CityIntelligence | null {
return key ? CITY_INTELLIGENCE[key] : null
}
/** Median-Marktmiete in CHF/m²/**Jahr** (Daten sind monatlich gespeichert → ×12), passend zu property.rentPricePerSqm. */
export function getMarketRent(city: string, assetType: string): number | null {
const intel = getCityIntelligence(city)
if (!intel) return null
if (assetType === 'OFFICE') return intel.medianRentOffice
if (assetType === 'LOGISTICS' || assetType === 'LIGHT_INDUSTRIAL') return intel.medianRentLogistics
if (assetType === 'RETAIL') return intel.medianRentRetail
return intel.medianRentOffice
const monthly =
assetType === 'LOGISTICS' || assetType === 'LIGHT_INDUSTRIAL' ? intel.medianRentLogistics
: assetType === 'RETAIL' ? intel.medianRentRetail
: intel.medianRentOffice
return monthly * 12
}
// ── City coordinates (WGS84) ─────────────────────────────────────────────────