investment_simulator/simulator.ipynb

183 lines
142 KiB
Plaintext
Raw Normal View History

2024-08-08 11:03:14 +10:00
{
"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
}