183 lines
142 KiB
Plaintext
183 lines
142 KiB
Plaintext
|
{
|
||
|
"cells": [
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 19,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"from simulator import *\n",
|
||
|
"\n",
|
||
|
"# Loan properties\n",
|
||
|
"PERIODS_PER_YEAR = 12\n",
|
||
|
"LOAN_DURATION_YEARS = 30\n",
|
||
|
"TOTAL_LOAN_PERIODS = PERIODS_PER_YEAR * LOAN_DURATION_YEARS\n",
|
||
|
"NOMINAL_ANNUAL_INTEREST_RATE = 6.6 / 100\n",
|
||
|
"LOAN_PRINCIPAL = 600000\n",
|
||
|
"PERIOD_INTEREST_RATE = NOMINAL_ANNUAL_INTEREST_RATE / PERIODS_PER_YEAR\n",
|
||
|
"PERIOD_PAYMENT = (LOAN_PRINCIPAL * (PERIOD_INTEREST_RATE * (1 + PERIOD_INTEREST_RATE) ** TOTAL_LOAN_PERIODS) /\n",
|
||
|
" ((1 + PERIOD_INTEREST_RATE) ** TOTAL_LOAN_PERIODS - 1))\n",
|
||
|
"\n",
|
||
|
"def calculate_total_payments(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"total_payments\"] + PERIOD_PAYMENT\n",
|
||
|
"\n",
|
||
|
"def calculate_interest_payment(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"remaining_loan\"] * PERIOD_INTEREST_RATE\n",
|
||
|
"\n",
|
||
|
"def calculate_total_interest_paid(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"total_interest_paid\"] + calculate_interest_payment(metrics)\n",
|
||
|
"\n",
|
||
|
"def calculate_principal_payment(metrics: MetricsContainer) -> float:\n",
|
||
|
" return PERIOD_PAYMENT - calculate_interest_payment(metrics)\n",
|
||
|
"\n",
|
||
|
"def calculate_remaining_loan(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"remaining_loan\"] - calculate_principal_payment(metrics)\n",
|
||
|
"\n",
|
||
|
"loan_metric_configs = {\n",
|
||
|
" \"total_payments\": MetricConfig(initial_value=0, period_calculator=calculate_total_payments),\n",
|
||
|
" \"interest_payment\": MetricConfig(initial_value=None, period_calculator=calculate_interest_payment, plot=False),\n",
|
||
|
" \"total_interest_paid\": MetricConfig(initial_value=0, period_calculator=calculate_total_interest_paid),\n",
|
||
|
" \"principal_payment\": MetricConfig(initial_value=None, period_calculator=calculate_principal_payment, plot=False),\n",
|
||
|
" \"remaining_loan\": MetricConfig(initial_value=LOAN_PRINCIPAL, period_calculator=calculate_remaining_loan)\n",
|
||
|
"}"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 20,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Property properties\n",
|
||
|
"DEPOSIT = 32500\n",
|
||
|
"ADDITIONAL_UPFRONT_COSTS = 11356\n",
|
||
|
"ANNUAL_APPRECIATION_RATE = 5 / 100\n",
|
||
|
"INITIAL_ANNUAL_RENTAL_INCOME = 14400\n",
|
||
|
"RENTAL_INCOME_ANNUAL_GROWTH_RATE = 6 / 100\n",
|
||
|
"INITIAL_PROPERTY_VALUE = LOAN_PRINCIPAL + DEPOSIT\n",
|
||
|
"PERIOD_APPRECIATION_RATE = (1 + ANNUAL_APPRECIATION_RATE) ** (1 / PERIODS_PER_YEAR) - 1\n",
|
||
|
"\n",
|
||
|
"def calculate_period_appreciation(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"property_value\"] * (1 + PERIOD_APPRECIATION_RATE) - metrics[\"property_value\"]\n",
|
||
|
"\n",
|
||
|
"def calculate_property_value(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"property_value\"] + calculate_period_appreciation(metrics)\n",
|
||
|
"\n",
|
||
|
"def calculate_equity(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"equity\"] + calculate_principal_payment(metrics) + calculate_period_appreciation(metrics)\n",
|
||
|
"\n",
|
||
|
"property_metric_configs = {\n",
|
||
|
" \"property_value\": MetricConfig(initial_value=INITIAL_PROPERTY_VALUE, period_calculator=calculate_property_value),\n",
|
||
|
" \"equity\": MetricConfig(initial_value=DEPOSIT, period_calculator=calculate_equity)\n",
|
||
|
"}"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 21,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Income properties\n",
|
||
|
"PERIOD_RENTAL_INCOME_GROWTH_RATE = (1 + RENTAL_INCOME_ANNUAL_GROWTH_RATE) ** (1 / PERIODS_PER_YEAR) - 1\n",
|
||
|
"INITIAL_PERIOD_RENTAL_INCOME = INITIAL_ANNUAL_RENTAL_INCOME / PERIODS_PER_YEAR\n",
|
||
|
"\n",
|
||
|
"def calculate_period_rental_income(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"period_rental_income\"] * (1 + PERIOD_RENTAL_INCOME_GROWTH_RATE)\n",
|
||
|
"\n",
|
||
|
"def calculate_total_rental_income(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"total_rental_income\"] + calculate_period_rental_income(metrics)\n",
|
||
|
"\n",
|
||
|
"def calculate_period_cashflow(metrics: MetricsContainer) -> float:\n",
|
||
|
" return calculate_period_rental_income(metrics) - calculate_interest_payment(metrics)\n",
|
||
|
"\n",
|
||
|
"def calculate_net_worth(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"net_worth\"] + calculate_period_cashflow(metrics) + calculate_period_appreciation(metrics) + calculate_principal_payment(metrics)\n",
|
||
|
"\n",
|
||
|
"income_metric_configs = {\n",
|
||
|
" \"period_rental_income\": MetricConfig(initial_value=INITIAL_PERIOD_RENTAL_INCOME, period_calculator=calculate_period_rental_income, plot=False),\n",
|
||
|
" \"total_rental_income\": MetricConfig(initial_value=0, period_calculator=calculate_total_rental_income),\n",
|
||
|
" \"period_cashflow\": MetricConfig(initial_value=None, period_calculator=calculate_period_cashflow, plot=False),\n",
|
||
|
" \"net_worth\": MetricConfig(initial_value=DEPOSIT - ADDITIONAL_UPFRONT_COSTS, period_calculator=calculate_net_worth),\n",
|
||
|
"}"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 22,
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"source": [
|
||
|
"# Baseline Properties\n",
|
||
|
"ETF_ANNUAL_YIELD = 9 / 100\n",
|
||
|
"ETF_PERIOD_YIELD = (1 + ETF_ANNUAL_YIELD) ** (1 / PERIODS_PER_YEAR) - 1\n",
|
||
|
"RENTAL_COST_ANNUAL_GROWTH_RATE = 6 / 100\n",
|
||
|
"INITIAL_ANNUAL_RENTAL_COST = 20400\n",
|
||
|
"INITIAL_PERIOD_RENTAL_COST = INITIAL_ANNUAL_RENTAL_COST / PERIODS_PER_YEAR\n",
|
||
|
"PERIOD_RENTAL_COST_GROWTH_RATE = (1 + RENTAL_COST_ANNUAL_GROWTH_RATE) ** (1 / PERIODS_PER_YEAR) - 1\n",
|
||
|
"\n",
|
||
|
"def calculate_period_rental_cost(metrics: MetricsContainer) -> float:\n",
|
||
|
" return metrics[\"period_rental_cost\"] * (1 + PERIOD_RENTAL_COST_GROWTH_RATE)\n",
|
||
|
"\n",
|
||
|
"def calculate_total_etf_value(metrics: MetricsContainer) -> float:\n",
|
||
|
" rental_cost_period = calculate_period_rental_cost(metrics)\n",
|
||
|
" return metrics[\"total_etf_value\"] * (1 + ETF_PERIOD_YIELD) + max(PERIOD_PAYMENT - rental_cost_period, 0)\n",
|
||
|
"\n",
|
||
|
"baseline_metric_configs = {\n",
|
||
|
" \"period_rental_cost\": MetricConfig(initial_value=INITIAL_PERIOD_RENTAL_COST, period_calculator=calculate_period_rental_cost, plot=False),\n",
|
||
|
" \"total_etf_value\": MetricConfig(initial_value=DEPOSIT + ADDITIONAL_UPFRONT_COSTS, period_calculator=calculate_total_etf_value),\n",
|
||
|
"}"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 23,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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
|
||
|
"text/plain": [
|
||
|
"<Figure size 1400x800 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"metric_configs = {\n",
|
||
|
" **loan_metric_configs,\n",
|
||
|
" **property_metric_configs,\n",
|
||
|
" **income_metric_configs,\n",
|
||
|
" **baseline_metric_configs\n",
|
||
|
"}\n",
|
||
|
"\n",
|
||
|
"simulate(TOTAL_LOAN_PERIODS, metric_configs)"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"metadata": {
|
||
|
"kernelspec": {
|
||
|
"display_name": "investment-simulator-KPfGkdIO-py3.12",
|
||
|
"language": "python",
|
||
|
"name": "python3"
|
||
|
},
|
||
|
"language_info": {
|
||
|
"codemirror_mode": {
|
||
|
"name": "ipython",
|
||
|
"version": 3
|
||
|
},
|
||
|
"file_extension": ".py",
|
||
|
"mimetype": "text/x-python",
|
||
|
"name": "python",
|
||
|
"nbconvert_exporter": "python",
|
||
|
"pygments_lexer": "ipython3",
|
||
|
"version": "3.12.0"
|
||
|
},
|
||
|
"orig_nbformat": 4
|
||
|
},
|
||
|
"nbformat": 4,
|
||
|
"nbformat_minor": 2
|
||
|
}
|