Immohai Datenmodell-Spezifikation¶
Status: verbindlich
Spezifikationsversion: 1.1.1
Modellbezug: 0.6.2
Implementierungsbezug: frontend-mvp-0.2.5
Stand: 2026-07-05
1. Zweck und Rang¶
Diese Datei ist die führende Wahrheit für Datenstrukturen und Feldverträge. Formeln stehen ausschließlich in docs/calculations/model-specification.md. Übergänge zwischen Eingabe, Szenarioauflösung, Engine, Speicherung und Reporting stehen in docs/specification/model-and-calculations/calculation-interfaces.md.
Diese Datei definiert:
kanonische Datenobjekte
Pflicht- und optionale Felder
zulässige Enumwerte
Versionierungsfelder
Snapshot- und Herkunftsregeln
Legacy-Lesealiase
Grenzen zwischen persönlichen Analysen, Mietdaten und Batchdaten
2. Grundsätze¶
Nur PropertyInput wird berechnet.
Quick, Expert und Batch verwenden dasselbe Modell.
Rohdaten werden nie direkt berechnet.
Berechnung und Speicherung sind getrennt.
Legacy-Aliase werden nur gelesen.
PropertyRecords sind unveränderbare Analysesnapshots.
Fachliche Feldnamen werden nicht je UI oder Report neu erfunden.
Reports und Vergleiche verwenden berechnete oder gespeicherte Snapshots.
3. Aktuelle Verträge und Versionen¶
PropertyInput: 0.5.1
AnalysisResult: 0.1.1
PropertyRecord: 0.1.1
RentEstimate: 0.1.0
MarketRentImport: 0.3.0
AnalysisDataQualityAssessment: 0.2.0
AnalysisDataQualityMethod: analysis-dq-0.2.0
Maschinenlesbare Verträge:
model/schema/property-input.schema.json
model/schema/analysis-result.schema.json
model/schema/property-record.schema.json
model/schema/rent-estimate.schema.json
model/schema/market-rent-import.schema.json
model/schema/analysis-data-quality.schema.json
Die zusammengehörigen Versionen stehen in model/config/model-manifest.json. Frontend-Kopien unter frontend/assets/config/ müssen bytegleich sein.
4. Versionierungsfelder¶
PropertyInput.meta.schema_version
= PropertyInput-Schemaversion
AnalysisResult.property_input_schema_version
AnalysisResult.analysis_result_schema_version
PropertyRecord.property_input_schema_version
PropertyRecord.analysis_result_schema_version
PropertyRecord.property_record_schema_version
Zusätzliche Konfigurations- und Methodenfelder:
default_assumptions_version
scenario_presets_version
object_types_version
market_rent_schema_version
rent_estimate_schema_version
analysis_data_quality_method_version
Das übergeordnete Feld schema_version auf AnalysisResult oder PropertyRecord ist nur ein nicht serialisierter Legacy-Lesealias für property_input_schema_version. Neue kanonische Ergebnisse schreiben es nicht.
5. ValueWithOrigin¶
Jeder berechnungsrelevante Eingabewert wird als ValueWithOrigin geführt.
Pflichtfelder:
value
unit
origin
source
scenario_policy
Optional:
confidence
note
evidence
value_status
origin:
manual
imported
scraped
estimated
derived
default
scenario
object_type_adjusted
missing
scenario_policy:
fixed
scenario_adjustable
sensitivity_only
value_status:
final
provisional
Regeln:
origin = missing -> value = null
value = null -> origin = missing
value_status = final -> scenario_policy = fixed
manuelle Eingabe allein bedeutet nicht automatisch final
source benennt die konkrete Herkunft oder den erzeugenden Schritt
confidence liegt, soweit numerisch geführt, zwischen 0 und 1
6. Fachstatus für Miete und Zins¶
6.1 rent_basis¶
current_contract_rent
market_rent_estimate
target_rent
manual_assumption
Standard-Policy:
current_contract_rent -> fixed
market_rent_estimate -> scenario_adjustable
target_rent -> scenario_adjustable
manual_assumption -> scenario_adjustable, außer value_status = final
6.2 interest_basis¶
binding_offer
indicative_market_rate
manual_assumption
Standard-Policy:
binding_offer -> fixed
indicative_market_rate -> scenario_adjustable
manual_assumption -> scenario_adjustable, außer value_status = final
interest_reset_basis verwendet dieselben Werte und Regeln für den Zins nach dem Reset.
7. ListingSourceReference¶
Ein Inseratslink wird ausschließlich als Quellenreferenz gespeichert.
Kanonische Struktur:
source_kind = listing_link
source_url
source_platform
processing_status = reference_only
captured_at
note optional
Verwendung:
PropertyInput.meta.source_reference
PropertyRecord.source_reference
PropertyRecord.listing_import_reference
Regeln:
source_url ist eine gültige URI.
source_platform benennt die Quelle nachvollziehbar.
captured_at ist ein ISO-8601-Zeitstempel.
processing_status bleibt reference_only.
Es erfolgt keine automatische Portalabfrage.
Es erfolgt keine automatische Feldübernahme.
Webseiteninhalt wird nicht direkt berechnet.
listing_import_state bleibt ausschließlich ein Legacy-Lesealias.
8. PropertyInput 0.5.1¶
Gruppen:
meta
base_data
acquisition
financing
rental
costs
tax optional
assumptions
scenarios
checklist optional
sources optional
notes optional
Neue PropertyInputs schreiben ausschließlich kanonische Felder. Die Root-Struktur erlaubt keine unbekannten zusätzlichen Gruppen.
8.1 meta¶
property_input_id
model_version
calculation_engine_version
schema_version
created_at
input_mode
analysis_origin
input_channel
object_id optional
updated_at optional
source_reference optional
data_quality_score optional
data_quality_assessment optional
input_mode:
quick
expert
batch
analysis_origin:
manual_analysis
batch_analysis
imported_analysis
Regeln:
property_input_id ist nicht leer.
model_version = 0.6.2.
schema_version = 0.5.1.
created_at und updated_at verwenden ISO 8601.
object_id wird nur als Referenz gelesen und nicht nachträglich in einen berechneten Snapshot geschrieben.
source_reference erfüllt bei Vorhandensein den ListingSourceReference-Vertrag.
data_quality_score liegt zwischen 0 und 100 oder ist null.
8.2 base_data¶
Pflichtfelder:
purchase_price
living_area_sqm
property_category
rental_strategy
construction_status
assumption_profile
Optionale Felder:
rooms
construction_year
condition_class
city
district
postal_code
location_label
object_type_profile
property_category:
apartment
single_family_house
two_family_house
multi_family_house
commercial
mixed_use
rental_strategy:
standard_rental
shared_flat
short_term_rental
construction_status:
existing
new_build
object_type_profile ist eine UI- und Übergangshilfe. Neue kanonische Datensätze führen zusätzlich die getrennten Dimensionen property_category, rental_strategy und construction_status.
8.3 acquisition¶
Pflichtfelder:
closing_costs_pct
initial_renovation_costs
Optional:
cash_reserve_after_closing
Regeln:
closing_costs_pct liegt zwischen 0 und 1.
initial_renovation_costs ist nicht negativ.
cash_reserve_after_closing ist ein Informations- und Due-Diligence-Feld.
8.4 financing¶
Pflichtfelder:
requested_equity_contribution
interest_initial_pct
interest_basis
amortization_initial_pct
interest_reset_year
interest_reset_pct
interest_reset_basis
Regeln:
Geld- und Prozentsatzwerte sind nicht negativ.
interest_reset_year ist eine positive Ganzzahl.
interest_basis und interest_reset_basis bestimmen die Standard-Szenario-Policy.
8.5 rental¶
Pflichtfelder:
monthly_cold_rent
rent_basis
Optional:
rent_estimate_reference
Regeln:
monthly_cold_rent ist nicht negativ.
market_rent_estimate benötigt eine RentEstimateReference.
current_contract_rent verwendet standardmäßig fixed.
8.6 costs¶
Pflichtfelder:
non_recoverable_operating_costs_pa
reserve_contribution_pa
recurring_capex_method
vacancy_pct
Konditional oder optional:
recurring_capex_pct_of_gross_rent conditional
recurring_capex_fixed_pa conditional
special_capex optional
house_fee_monthly optional
recurring_capex_method:
percent_of_gross_rent
fixed_annual
Methodenregeln:
percent_of_gross_rent -> recurring_capex_pct_of_gross_rent ist erforderlich
percent_of_gross_rent -> recurring_capex_fixed_pa ist 0 oder nicht vorhanden
fixed_annual -> recurring_capex_fixed_pa ist erforderlich
fixed_annual -> recurring_capex_pct_of_gross_rent ist 0 oder nicht vorhanden
special_capex[]:
event_id
year
amount
description optional
origin optional
source optional
event_id ist nicht leer, year ist positiv und amount ist nicht negativ.
8.7 tax¶
tax ist ein optionaler, modularer Bereich. Er darf zusätzliche Felder enthalten, solange diese nicht als Teil der MVP-Hauptkennzahl Cashflow vor Steuer interpretiert werden.
8.8 assumptions¶
Pflichtfelder:
term_years
inflation_pct
rent_growth_pct
value_growth_pct
etf_return_pct
selling_costs_pct
Regeln:
term_years ist eine positive Ganzzahl.
inflation_pct und etf_return_pct sind nicht negativ.
selling_costs_pct liegt zwischen 0 und 1.
etf_return_pct verwendet scenario_policy = sensitivity_only.
8.9 scenarios¶
scenario_order = [Best, Base, Stress]
selected_base_scenario = Base
control_mode = preset | custom_sliders
controls
overrides
Bei custom_sliders sind alle sieben Steuerwerte Pflicht:
rent_variation_pct
interest_variation_pct
rent_growth_variation_pct
value_growth_variation_pct
inflation_variation_pct
vacancy_variation_pct
capex_variation_pct
Alle Steuerwerte sind ValueWithOrigin und liegen als Dezimalbruch zwischen 0 und 1.
overrides darf nur enthalten:
Best optional
Stress optional
Base-Overrides und ETF-Overrides sind nicht zulässig. Ein Feldoverride enthält mindestens:
value
origin = scenario
base_value
source
note optional
8.10 checklist, sources und notes¶
checklist:
strukturierte Due-Diligence- und Prüfinformationen
sources:
nachvollziehbare Quelleneinträge
notes:
ergänzende, nicht berechnende Hinweise
Diese Bereiche dürfen keine alternative Berechnungslogik enthalten.
9. AnalysisDataQualityAssessment 0.2.0¶
Führende Methode: docs/specification/data-quality-and-validation/data-quality-assessment.md.
data_quality_score_total
required_fields_score
location_match_score
rent_estimate_score
cost_data_score
condition_score
financing_data_score
status
warnings
method_version
status:
good
borderline
insufficient
Speicherung:
PropertyInput.meta.data_quality_assessment
AnalysisResult.data_quality_assessment
PropertyRecord.data_quality_assessment
Regeln:
alle Scores liegen zwischen 0 und 100
method_version = analysis-dq-0.2.0
fehlende oder unzureichende Datenqualität führt zu UNRATED
Datenqualität verändert keine Finanzformel
10. RentEstimate 0.1.0¶
Führender Vertrag: docs/specification/product-domains-and-workflows/rent-analysis.md.
Gemeinsame Pflichtfelder:
rent_estimate_id
created_at
estimate_type
source_type
lookup_method
matched_location_level
cold_rent_per_sqm
monthly_cold_rent
confidence
confidence_level
confidence_method_version
required_fields_missing
warnings
Optionale oder quellabhängige Felder:
rent_reference_dataset_id
rent_reference_dataset_version
rent_reference_dataset_scope_id
matched_group_fields
p25_eur_per_sqm
median_eur_per_sqm
p75_eur_per_sqm
sample_size
data_from
data_until
fallback_reason
estimate_type:
official_rent_reference
market_rent_estimate
wg_estimate
source_type:
official_rent_reference
market_rent_dataset
wg_estimate
Semantik:
confidence = Zahl 0 bis 1
confidence_level = high | medium | low | insufficient
Für market_rent_estimate sind Dataset-ID, Version, Scope, Gruppierungsfelder, P25, Median, P75 und Samplegröße Pflicht. Für offizielle Einzelreferenzen und WG-Schätzungen sind diese Felder nur zu führen, wenn sie fachlich vorhanden sind.
11. RentEstimateReference¶
Die im PropertyInput gespeicherte Referenz verwendet dieselben kanonischen Namen.
Gemeinsame Pflichtfelder:
rent_estimate_id
estimate_type
source_type
matched_location_level
matched_group_fields
confidence
confidence_level
confidence_method_version
warnings
Optional:
rent_reference_dataset_id
rent_reference_dataset_version
rent_reference_dataset_scope_id
lookup_method
p25_eur_per_sqm
median_eur_per_sqm
p75_eur_per_sqm
sample_size
data_from
data_until
fallback_reason
Für estimate_type = market_rent_estimate zusätzlich Pflicht:
rent_reference_dataset_id
rent_reference_dataset_version
rent_reference_dataset_scope_id
matched_group_fields
p25_eur_per_sqm
median_eur_per_sqm
p75_eur_per_sqm
sample_size
Nicht zulässig in neuen Datensätzen:
source_dataset_id
source_dataset_version
match_level
Diese Namen bleiben ausschließlich Legacy-Lesealiase.
12. ScenarioInput¶
ScenarioInput ist die vollständig aufgelöste Recheneingabe für genau ein Szenario.
Es enthält:
kanonische Zahlenwerte
fachliche Basisfelder
Herkunftsmetadaten
Szenario-Policies
Szenariosteuerungsart
Datenqualität
Modell-, Engine-, Schema- und Konfigurationsversionen
scenario_name
ScenarioInput ist kein dauerhaftes Nutzereingabeformat und wird nicht direkt aus Rohdaten oder DOM-Feldern erzeugt.
13. AnnualProjectionRow¶
Jede Jahreszeile enthält:
scenario_name
year
property_value
gross_rent
vacancy_loss
non_recoverable_operating_costs
reserve_contribution
recurring_capex
special_capex
noi
dscr_numerator
dscr
interest_rate
interest
principal
debt_service
debt_start
debt
current_ltv
cashflow_before_tax
annual_support_required
cumulative_support_required
side_account
property_equity_value
property_wealth_before_exit
net_vs_etf_before_exit
selling_costs
net_sale_proceeds
terminal_property_wealth
terminal_net_vs_etf
etf_value
Regeln:
year ist eine positive Ganzzahl.
scenario_name ist Best, Base oder Stress.
Nicht anwendbare Werte werden als null und nicht als erfundene Zahl gespeichert.
14. Kanonische KPIs¶
purchase_price
term_years
initial_renovation_costs
purchase_price_per_sqm
rent_per_sqm_month
closing_costs
total_acquisition_cost
total_initial_investment
requested_equity_contribution
equity_contributed
excess_available_equity
loan_amount
non_purchase_costs_financed_amount
initial_ltv
current_ltv_y1
gross_rent_y1
gross_yield_y1
noi_yield_y1
purchase_price_factor
noi_y1
reserve_contribution_y1
recurring_capex_y1
debt_service_y1
dscr_y1
cashflow_before_tax_y1
minimum_cashflow
cumulative_support_required
first_positive_cashflow_year
durable_self_funding_year
property_wealth_before_exit_end
terminal_property_wealth_end
etf_value_end
net_vs_etf_before_exit_end
terminal_net_vs_etf_after_exit
first_etf_break_even_year
durable_etf_break_even_year
selling_costs_end
data_quality_score
KPIs werden auf ScenarioResult-Ebene geführt. base_summary_kpis ist die unveränderte Base-Zusammenfassung im AnalysisResult.
15. KpiAssessment¶
Ein KPI-Assessment enthält mindestens:
status
label
reason
threshold_summary optional
Schwellen und fachliche Gründe stammen ausschließlich aus docs/calculations/model-specification.md und der Engine. UI und Reports leiten keine eigenen Assessments ab.
16. ScenarioAssessment¶
cashflow_status
dscr_status
liquidity_status
terminal_etf_status
critical_findings
ScenarioAssessment fasst Case-bezogene Ergebnisdimensionen zusammen und ist kein vollständiges Investment Rating.
17. ScenarioRating¶
scenario_rating
reason
hard_critical_findings
scenario_rating:
GREEN
YELLOW
RED
UNRATED
ScenarioRating dient ausschließlich der sichtbaren Case-Bewertung im Szenariovergleich. Es ist kein InvestmentRating und beeinflusst keine andere Berechnung.
18. ScenarioResult¶
scenario_name
projection
kpis
kpi_assessments
scenario_assessment
scenario_rating
plausibility_checks
warnings
Regeln:
scenario_name ist Best, Base oder Stress.
projection enthält ausschließlich AnnualProjectionRows.
kpis verwendet nur kanonische KPI-Namen.
kpi_assessments verwendet Engine-Ergebnisse.
Kein ScenarioResult enthält ein serialisiertes investment_rating oder generisches rating.
19. InvestmentRating¶
investment_rating
reason
components
hard_critical_findings
investment_rating:
GREEN
YELLOW
RED
UNRATED
InvestmentRating existiert genau einmal auf AnalysisResult-Ebene. Seine Regeln stehen ausschließlich in docs/calculations/model-specification.md.
20. AnalysisResult 0.1.1¶
analysis_result_id
property_input_id
property_input
model_version
calculation_engine_version
property_input_schema_version
analysis_result_schema_version
default_assumptions_version
scenario_presets_version
object_types_version
market_rent_schema_version optional
rent_estimate_schema_version optional
analysis_data_quality_method_version
created_at
scenario_results
base_summary_kpis
investment_rating
data_quality_assessment
warnings
calculation_status
calculation_status:
calculated
calculated_with_warnings
failed
Regeln:
analysis_result_id und property_input_id sind nicht leer.
property_input ist die unveränderbare Kopie des tatsächlich berechneten PropertyInput.
property_input_id = property_input.meta.property_input_id.
property_input erfüllt PropertyInput 0.5.1.
scenario_results enthält Best, Base und Stress.
base_summary_kpis stammt aus Base.
InvestmentRating wird genau einmal auf AnalysisResult-Ebene gespeichert.
Das Formular wird nach dem Rechenlauf nicht erneut als Eingabesnapshot interpretiert.
Runtime-Aliase wie results, baseResult und schema_version sind nicht enumerable und werden nicht serialisiert.
Snapshot-Metadaten wie snapshot_source und snapshot_object_id sind nicht Teil des serialisierten Vertrags.
21. PropertyRecord 0.1.1¶
object_id
analysis_result_id
display_name
created_at
updated_at
created_by optional
updated_by optional
owner_user_id optional
owner_display_name optional
model_version
calculation_engine_version
property_input_schema_version
analysis_result_schema_version
property_record_schema_version
default_assumptions_version
scenario_presets_version
object_types_version
market_rent_schema_version optional
rent_estimate_schema_version optional
analysis_data_quality_method_version
source_type
source_reference optional
batch_run_id optional
batch_row_id optional
listing_import_reference optional
status
tags
location
property_input
scenario_results
summary_kpis
rating
data_quality_assessment
plausibility_checks
warnings
calculation_status
notes
source_type:
manual_analysis
batch_analysis
imported_analysis
status:
watchlist
in_review
rejected
archived
location:
city
district
postal_code
location_label
Regeln:
object_id, analysis_result_id und display_name sind nicht leer.
property_record_schema_version = 0.1.1.
property_input erfüllt PropertyInput 0.5.1.
scenario_results erfüllt den ScenarioResult-Vertrag.
summary_kpis erfüllt den kanonischen KPI-Vertrag.
rating enthält ausschließlich das InvestmentRating.
source_reference und listing_import_reference erfüllen bei Vorhandensein ListingSourceReference.
object_id wird nicht in den gebundenen PropertyInput-Snapshot geschrieben.
Berechnen allein erzeugt keinen PropertyRecord.
Speichern erfolgt ausschließlich durch bewusste Nutzeraktion.
Beim Öffnen:
der gespeicherte Snapshot wird angezeigt
es startet kein automatischer Rechenlauf
alle im Formular repräsentierbaren Werte werden wiederhergestellt
Szenario-Steuerungsmodus und alle sieben Sliderwerte werden wiederhergestellt
nicht sichtbare Snapshot-Annahmen bleiben für eine bewusste Neuberechnung erhalten
Versionsunterschiede bleiben sichtbar
Eine bewusste Neuberechnung erzeugt ein neues PropertyInput und AnalysisResult, verändert aber den alten PropertyRecord nicht automatisch.
22. Warnungen und Plausibilitätschecks¶
Warnungen enthalten mindestens:
code
severity
message
Optional:
affected_fields
scenario_name
weitere strukturierte Kontextfelder
severity für aggregierte Warnungen:
info
warning
critical
high
medium
low
Plausibilitätschecks enthalten mindestens:
code
severity = info | warning | critical
message
affected_fields optional
Warnungen und Plausibilitätschecks erklären Ergebnisse, verändern aber keine Formel.
23. Legacy-Lesealiase¶
monthly_rent_market -> monthly_cold_rent
rent_mode -> rent_basis nur über explizite Migrationsabbildung
annual_gross_rent_y1 -> gross_rent_y1
net_yield_before_financing_y1 -> noi_yield_y1
ltv_y1 -> current_ltv_y1
net_vs_etf_end -> terminal_net_vs_etf_after_exit
property_wealth_end -> terminal_property_wealth_end
equity -> requested_equity_contribution oder equity_contributed je Altvertrag
schema_version auf AnalysisResult/PropertyRecord -> property_input_schema_version
source_dataset_id -> rent_reference_dataset_id
source_dataset_version -> rent_reference_dataset_version
match_level -> matched_location_level
listing_import_state -> listing_import_reference
non_recoverable_costs -> non_recoverable_operating_costs
running_capex -> recurring_capex
property_wealth -> terminal_property_wealth
net_vs_etf -> terminal_net_vs_etf
Regeln:
Legacy-Aliase sind nicht enumerable, soweit sie zur Laufzeit bereitgestellt werden.
Neue kanonische Datensätze schreiben keine Legacy-Aliase.
Eine dauerhafte Migration erfolgt nur explizit und versioniert.
24. Weitere Datenbereiche¶
24.1 Marktmiete¶
RentImportRun
RentObservation
RentAreaStats
RentReferenceDataset
RentEstimate
Diese Datenbereiche gehören zur Mietreferenzverwaltung und werden nicht als persönliche PropertyRecords gespeichert.
24.2 Batch¶
BatchImportRun
BatchImportRow
NormalizedListing
BatchAnalysisResult
Roh- und normalisierte Batchdaten werden nicht direkt zu PropertyRecords. Erst eine bewusste Übernahme eines vollständig berechneten Ergebnisses erzeugt einen PropertyRecord.
24.3 FinancingProfile¶
Ein späteres FinancingProfile ist eine versionierte Eingabehilfe zur Erzeugung normaler PropertyInputs. Es ist kein zweites Berechnungsmodell. Der Detailvertrag ist noch nicht verbindlich festgelegt.
25. Speicher¶
Aktuelle lokale Speicher:
immohai.propertyRecords.v1
immohai.marketRentDatabase.v1
Storage Keys werden nur über eine explizite Migration geändert.
26. Validierung¶
Verbindliche Prüfungen stehen in docs/specification/data-quality-and-validation/validation.md.
Mindestens zu validieren:
vollständige Schemaerfüllung
semantische ValueWithOrigin-Regeln
konditionale CAPEX-Felder
konditionale RentEstimateReference-Felder
Szenario-Reihenfolge und Sliderpflichten
ListingSourceReference
Snapshot-Identitäten
Nichtserialisierung von Runtime- und Legacy-Aliasen
PropertyRecord-Wiederherstellung und bewusste Neuberechnung