mirror of
https://github.com/Hestia-Homes/Model.git
synced 2026-06-08 11:17:27 +00:00
Added further placeholder valuation figures and added mvp valuation increase methodology
This commit is contained in:
parent
a44912defa
commit
84bcb99ad9
4 changed files with 105 additions and 10 deletions
2
.idea/Model.iml
generated
2
.idea/Model.iml
generated
|
|
@ -7,7 +7,7 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/open_uprn" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/recommendations" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (model_data)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.10 (backend)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyNamespacePackagesService">
|
||||
|
|
|
|||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
|
|
@ -3,7 +3,7 @@
|
|||
<component name="Black">
|
||||
<option name="sdkName" value="Python 3.10 (backend)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (model_data)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.10 (backend)" project-jdk-type="Python SDK" />
|
||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ class MaterialType(enum.Enum):
|
|||
low_energy_lighting_installation = "low_energy_lighting_installation"
|
||||
flat_roof_preparation = "flat_roof_preparation"
|
||||
flat_roof_vapour_barrier = "flat_roof_vapour_barrier"
|
||||
flat_roof_waterpoofing = "flat_roof_waterpoofing"
|
||||
flat_roof_waterproofing = "flat_roof_waterproofing"
|
||||
|
||||
|
||||
class DepthUnit(enum.Enum):
|
||||
|
|
|
|||
|
|
@ -1,22 +1,117 @@
|
|||
import numpy as np
|
||||
|
||||
|
||||
class PropertyValuation:
|
||||
"""
|
||||
This is a placeholder class for the property valuation model
|
||||
"""
|
||||
|
||||
UPRN_VALUE_LOOKUP = {
|
||||
15038202: {"current_value": 202000, "increase_percentage": 0.05725},
|
||||
37024763: {"current_value": 213000, "increase_percentage": 0.025},
|
||||
15038202: 202000,
|
||||
37024763: 213000,
|
||||
100070478545: 212000,
|
||||
100070297696: 235000,
|
||||
100070476394: 222000, # Based on Zoopla's estimation of next door, 20 Parkside
|
||||
100071264896: 128000,
|
||||
# Based on next door neighbour: https://themovemarket.com/tools/propertyprices/flat-2-queens-wood-house-219
|
||||
# -brandwood-road-birmingham-b14-6pu
|
||||
}
|
||||
|
||||
# We base our valuation uplifts on a number of sources
|
||||
# https://www.moneysupermarket.com/gas-and-electricity/value-of-efficiency/
|
||||
MSM_MAPPING = [
|
||||
{"start": "G", "end": "F", "increase_percentage": 0.06},
|
||||
{"start": "F", "end": "E", "increase_percentage": 0.01},
|
||||
{"start": "E", "end": "D", "increase_percentage": 0.01},
|
||||
{"start": "D", "end": "C", "increase_percentage": 0.02},
|
||||
{"start": "C", "end": "B", "increase_percentage": 0.04},
|
||||
{"start": "B", "end": "A", "increase_percentage": 0.0},
|
||||
]
|
||||
|
||||
# https://www.lloydsbankinggroup.com/media/press-releases/2021/halifax/homebuyers-pay-a-green-premium-of-40000
|
||||
# -for-the-most-energy-efficient-properties.html
|
||||
LLOYDS_MAPPING = [
|
||||
{"start": "G", "end": "F", "increase_percentage": 0.038},
|
||||
{"start": "F", "end": "E", "increase_percentage": 0.029},
|
||||
{"start": "E", "end": "D", "increase_percentage": 0.024},
|
||||
{"start": "D", "end": "C", "increase_percentage": 0.02},
|
||||
{"start": "C", "end": "B", "increase_percentage": 0.02},
|
||||
{"start": "B", "end": "A", "increase_percentage": 0.018},
|
||||
]
|
||||
|
||||
KNIGHT_FRANK_MAPPING = [
|
||||
{"start": "D", "end": "C", "increase_percentage": 0.03},
|
||||
{"start": "D", "end": "B", "increase_percentage": 0.088},
|
||||
]
|
||||
|
||||
NATIONWIDE_MAPPING = [
|
||||
{"start": "G", "end": "D", "increase_percentage": 0.035},
|
||||
{"start": "F", "end": "D", "increase_percentage": 0.035},
|
||||
{"start": "D", "end": "B", "increase_percentage": 0.017},
|
||||
{"start": "D", "end": "A", "increase_percentage": 0.017},
|
||||
]
|
||||
|
||||
EPC_BANDS = ["G", "F", "E", "D", "C", "B", "A"]
|
||||
|
||||
@classmethod
|
||||
def get_increase(cls, epc_band_range):
|
||||
|
||||
increases = []
|
||||
for i in range(len(epc_band_range)):
|
||||
|
||||
if i == len(epc_band_range) - 1:
|
||||
break
|
||||
|
||||
current = epc_band_range[i]
|
||||
next = epc_band_range[i + 1]
|
||||
|
||||
msm_increase = [x for x in cls.MSM_MAPPING if x["start"] == current and x["end"] == next][0]
|
||||
lloyds_increase = [x for x in cls.LLOYDS_MAPPING if x["start"] == current and x["end"] == next][0]
|
||||
|
||||
increases.append(
|
||||
{
|
||||
"start": current,
|
||||
"end": next,
|
||||
"msm_increase": msm_increase["increase_percentage"],
|
||||
"lloyds_increase": lloyds_increase["increase_percentage"],
|
||||
}
|
||||
)
|
||||
|
||||
# We now aggregate the increases. The should be compound increases so we multiply them together
|
||||
msm_increase = np.prod([1 + x["msm_increase"] for x in increases]) - 1
|
||||
lloyds_increase = np.prod([1 + x["lloyds_increase"] for x in increases]) - 1
|
||||
|
||||
return msm_increase, lloyds_increase
|
||||
|
||||
@classmethod
|
||||
def estimate(cls, property_instance, target_epc):
|
||||
data = cls.UPRN_VALUE_LOOKUP.get(property_instance.uprn)
|
||||
value = cls.UPRN_VALUE_LOOKUP.get(property_instance.uprn)
|
||||
|
||||
if not data:
|
||||
if not value:
|
||||
raise ValueError("Have not implemented valuation for this property")
|
||||
|
||||
new_valuation = (1 + data["increase_percentage"]) * data["current_value"]
|
||||
current_epc = property_instance.data["current-energy-rating"]
|
||||
# We get the spectrum of ratings between the current and target EPC
|
||||
epc_band_range = cls.EPC_BANDS[cls.EPC_BANDS.index(current_epc): cls.EPC_BANDS.index(target_epc) + 1]
|
||||
|
||||
increase = round(new_valuation - data["current_value"], 2)
|
||||
msm_increase, lloyds_increase = cls.get_increase(epc_band_range)
|
||||
|
||||
return increase
|
||||
# We now use the knight frank and nationwide data to get further valuation evidence, if we have it
|
||||
kf_increase = [x for x in cls.KNIGHT_FRANK_MAPPING if x["start"] == current_epc and x["end"] == target_epc]
|
||||
nw_increase = [x for x in cls.NATIONWIDE_MAPPING if x["start"] == current_epc and x["end"] == target_epc]
|
||||
|
||||
kf_increase = kf_increase[0]["increase_percentage"] if kf_increase else None
|
||||
nw_increase = nw_increase[0]["increase_percentage"] if nw_increase else None
|
||||
|
||||
all_increases = [x for x in [msm_increase, lloyds_increase, kf_increase, nw_increase] if x is not None]
|
||||
|
||||
max_increase = max(all_increases)
|
||||
min_increase = min(all_increases)
|
||||
avg_increase = np.mean(all_increases)
|
||||
|
||||
return {
|
||||
"current_value": value,
|
||||
"lower_bound_increased_value": value * (1 + min_increase),
|
||||
"upper_bound_increased_value": value * (1 + max_increase),
|
||||
"average_increased_value": value * (1 + avg_increase),
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue