Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import matplotlib.pyplot as plt
|
3 |
+
from matplotlib.ticker import FuncFormatter
|
4 |
+
import polars as pl
|
5 |
+
import gradio as gr
|
6 |
+
|
7 |
+
def calculate_mortgage(principal, annual_interest_rate, years, compound_yearly=True):
|
8 |
+
if compound_yearly:
|
9 |
+
# Convert annual rate to monthly effective rate for yearly compounding
|
10 |
+
monthly_interest_rate = (1 + annual_interest_rate) ** (1/12) - 1
|
11 |
+
else:
|
12 |
+
monthly_interest_rate = annual_interest_rate / 12
|
13 |
+
|
14 |
+
months = years * 12
|
15 |
+
if monthly_interest_rate == 0:
|
16 |
+
return principal / months
|
17 |
+
|
18 |
+
mortgage_payment = (
|
19 |
+
principal * monthly_interest_rate * (1 + monthly_interest_rate) ** months
|
20 |
+
) / ((1 + monthly_interest_rate) ** months - 1)
|
21 |
+
return mortgage_payment
|
22 |
+
|
23 |
+
def generate_amortization_schedule(principal, annual_interest_rate, years):
|
24 |
+
monthly_payment = calculate_mortgage(principal, annual_interest_rate, years)
|
25 |
+
monthly_rate = annual_interest_rate / 12
|
26 |
+
months = years * 12
|
27 |
+
schedule = []
|
28 |
+
remaining_balance = principal
|
29 |
+
|
30 |
+
for month in range(1, months + 1):
|
31 |
+
interest_payment = remaining_balance * monthly_rate
|
32 |
+
principal_payment = monthly_payment - interest_payment
|
33 |
+
remaining_balance -= principal_payment
|
34 |
+
schedule.append({
|
35 |
+
"month": month,
|
36 |
+
"remaining_balance": remaining_balance,
|
37 |
+
"payment": monthly_payment,
|
38 |
+
"principal": principal_payment,
|
39 |
+
"interest": interest_payment,
|
40 |
+
})
|
41 |
+
return schedule
|
42 |
+
|
43 |
+
class FlatInvestmentParameters:
|
44 |
+
flat_price = 2000000
|
45 |
+
one_time_payment = 100000
|
46 |
+
yearly_interest_rate = 0.05
|
47 |
+
mortgage_years = 30
|
48 |
+
initial_rent = 12000
|
49 |
+
yearly_rent_increase = 0.05
|
50 |
+
yearly_flat_appreciation = 0.07
|
51 |
+
down_payment_percentage = 0.1
|
52 |
+
|
53 |
+
def calculate_flat_investment(flatInvestmentParameters: FlatInvestmentParameters):
|
54 |
+
flat_price = flatInvestmentParameters.flat_price
|
55 |
+
one_time_payment = flatInvestmentParameters.one_time_payment
|
56 |
+
interest_rate = flatInvestmentParameters.yearly_interest_rate
|
57 |
+
mortgage_years = flatInvestmentParameters.mortgage_years
|
58 |
+
rent_current = flatInvestmentParameters.initial_rent
|
59 |
+
rent_increase = flatInvestmentParameters.yearly_rent_increase
|
60 |
+
flat_appreciation = flatInvestmentParameters.yearly_flat_appreciation
|
61 |
+
down_payment_percentage = flatInvestmentParameters.down_payment_percentage
|
62 |
+
|
63 |
+
down_payment = flat_price * down_payment_percentage
|
64 |
+
initial_investment = down_payment + one_time_payment
|
65 |
+
loan_amount = flat_price * (1 - down_payment_percentage)
|
66 |
+
|
67 |
+
amortization = generate_amortization_schedule(loan_amount, interest_rate, mortgage_years)
|
68 |
+
total_months = mortgage_years * 12
|
69 |
+
|
70 |
+
months = []
|
71 |
+
net_worth = []
|
72 |
+
cash_balance = [-initial_investment]
|
73 |
+
rent_received = []
|
74 |
+
cash_flow = []
|
75 |
+
|
76 |
+
for i, month_data in enumerate(amortization):
|
77 |
+
current_month = i + 1
|
78 |
+
current_year = (current_month - 1) // 12
|
79 |
+
current_year_float = (current_month - 1) / 12
|
80 |
+
|
81 |
+
current_rent = rent_current * (1 + rent_increase) ** current_year # Removed *0.9
|
82 |
+
current_flat_price = flat_price * (1 + flat_appreciation) ** current_year_float
|
83 |
+
rent_received.append(current_rent)
|
84 |
+
|
85 |
+
new_cash = cash_balance[-1] + current_rent - month_data["payment"]
|
86 |
+
cash_flow_this_month = current_rent - month_data["payment"]
|
87 |
+
cash_flow.append(cash_flow_this_month)
|
88 |
+
cash_balance.append(new_cash)
|
89 |
+
|
90 |
+
months.append(current_month)
|
91 |
+
current_net_worth = (current_flat_price - month_data["remaining_balance"]) + cash_balance[-1]
|
92 |
+
net_worth.append(current_net_worth)
|
93 |
+
|
94 |
+
debt = [month["remaining_balance"] for month in amortization]
|
95 |
+
cash_flow_cumulative = np.cumsum(cash_flow)
|
96 |
+
|
97 |
+
monthly_roi = []
|
98 |
+
for month in range(1, total_months + 1):
|
99 |
+
total_cashflow = sum(cash_flow[:month])
|
100 |
+
mortgage_current = debt[month-1]
|
101 |
+
flat_price_current = flat_price * (1 + flat_appreciation) ** ((month - 1) / 12) # Corrected
|
102 |
+
roi = (total_cashflow - mortgage_current + flat_price_current - initial_investment) / initial_investment
|
103 |
+
monthly_roi.append(roi)
|
104 |
+
|
105 |
+
values = [{
|
106 |
+
"month": i,
|
107 |
+
"rent": rent_received[i],
|
108 |
+
"payment": amortization[i]["payment"],
|
109 |
+
"cash_flow": cash_flow[i],
|
110 |
+
"debt": debt[i],
|
111 |
+
"cash_flow_cumulative": cash_flow_cumulative[i],
|
112 |
+
"flat_price": flat_price * (1 + flat_appreciation) ** (i / 12),
|
113 |
+
"net_worth": net_worth[i],
|
114 |
+
"roi": monthly_roi[i],
|
115 |
+
} for i in range(total_months)]
|
116 |
+
return values
|
117 |
+
|
118 |
+
|
119 |
+
def create_plots(investment_history, mortgage_years):
|
120 |
+
# Create formatter function
|
121 |
+
def currency(x, pos):
|
122 |
+
return f"{x/1000000:,.0f}M CZK"
|
123 |
+
|
124 |
+
# Create figure with 3 subplots
|
125 |
+
fig = plt.figure(figsize=(15, 12))
|
126 |
+
|
127 |
+
# First subplot - Yearly view
|
128 |
+
ax1 = plt.subplot(311)
|
129 |
+
months = investment_history["month"].to_numpy()
|
130 |
+
debt = investment_history["debt"].to_numpy()
|
131 |
+
flat_price = investment_history["flat_price"].to_numpy()
|
132 |
+
cash_flow_cumulative = investment_history["cash_flow_cumulative"].to_numpy()
|
133 |
+
net_worth = investment_history["net_worth"].to_numpy()
|
134 |
+
|
135 |
+
yearly_mask = [i % 12 == 11 for i in range(len(months))]
|
136 |
+
ax1.plot(months[yearly_mask] / 12, debt[yearly_mask], label="Debt")
|
137 |
+
ax1.plot(months[yearly_mask] / 12, flat_price[yearly_mask], label="Flat Price")
|
138 |
+
ax1.plot(months[yearly_mask] / 12, cash_flow_cumulative[yearly_mask], label="Total Cash Flow")
|
139 |
+
ax1.plot(months[yearly_mask] / 12, net_worth[yearly_mask], label="Net Worth")
|
140 |
+
ax1.set_title("Yearly View")
|
141 |
+
ax1.set_xlabel("Years")
|
142 |
+
ax1.legend()
|
143 |
+
ax1.yaxis.set_major_formatter(FuncFormatter(currency))
|
144 |
+
ax1.grid(True)
|
145 |
+
|
146 |
+
# Second subplot - Monthly view
|
147 |
+
ax2 = plt.subplot(312)
|
148 |
+
ax2.plot(months, debt, label="Debt")
|
149 |
+
ax2.plot(months, flat_price, label="Flat Price")
|
150 |
+
ax2.plot(months, cash_flow_cumulative, label="Total Cash Flow")
|
151 |
+
ax2.plot(months, net_worth, label="Net Worth")
|
152 |
+
ax2.set_title("Monthly View")
|
153 |
+
ax2.set_xlabel("Months")
|
154 |
+
ax2.legend()
|
155 |
+
ax2.yaxis.set_major_formatter(FuncFormatter(currency))
|
156 |
+
ax2.grid(True)
|
157 |
+
|
158 |
+
# Third subplot - ROI
|
159 |
+
ax3 = plt.subplot(313)
|
160 |
+
ax3.plot(range(1, mortgage_years + 1), investment_history["roi"].to_numpy()[yearly_mask])
|
161 |
+
ax3.set_title("Yearly ROI")
|
162 |
+
ax3.set_xlabel("Years")
|
163 |
+
ax3.set_ylabel("ROI")
|
164 |
+
ax3.yaxis.set_major_formatter(FuncFormatter(lambda x, _: f"{x:.0%}"))
|
165 |
+
ax3.grid(True)
|
166 |
+
|
167 |
+
plt.tight_layout()
|
168 |
+
return fig
|
169 |
+
|
170 |
+
def calculate_investment(
|
171 |
+
flat_price: float,
|
172 |
+
one_time_payment: float,
|
173 |
+
yearly_interest_rate: float,
|
174 |
+
mortgage_years: int,
|
175 |
+
initial_rent: float,
|
176 |
+
yearly_rent_increase: float,
|
177 |
+
yearly_flat_appreciation: float,
|
178 |
+
down_payment_percentage: float
|
179 |
+
):
|
180 |
+
params = FlatInvestmentParameters()
|
181 |
+
params.flat_price = flat_price
|
182 |
+
params.one_time_payment = one_time_payment
|
183 |
+
params.yearly_interest_rate = yearly_interest_rate
|
184 |
+
params.mortgage_years = mortgage_years
|
185 |
+
params.initial_rent = initial_rent
|
186 |
+
params.yearly_rent_increase = yearly_rent_increase
|
187 |
+
params.yearly_flat_appreciation = yearly_flat_appreciation
|
188 |
+
params.down_patment_percentage = down_payment_percentage
|
189 |
+
|
190 |
+
# Calculate initial investment components
|
191 |
+
down_payment = flat_price * down_payment_percentage
|
192 |
+
initial_investment = down_payment + one_time_payment
|
193 |
+
monthly_payment = calculate_mortgage(flat_price * (1 - down_payment_percentage), yearly_interest_rate, mortgage_years)
|
194 |
+
|
195 |
+
investment_summary = [
|
196 |
+
["Down Payment", f"{down_payment:,.0f} CZK"],
|
197 |
+
["One Time Payment", f"{one_time_payment:,.0f} CZK"],
|
198 |
+
["Total Initial Investment", f"{initial_investment:,.0f} CZK"],
|
199 |
+
["Monthly Payment", f"{monthly_payment:,.0f} CZK"]
|
200 |
+
]
|
201 |
+
|
202 |
+
investment_history = calculate_flat_investment(params)
|
203 |
+
investment_df = pl.DataFrame(investment_history)
|
204 |
+
|
205 |
+
plot = create_plots(investment_df, mortgage_years)
|
206 |
+
|
207 |
+
display_df = investment_df.select([
|
208 |
+
pl.col("month"),
|
209 |
+
pl.col("rent").round(0),
|
210 |
+
pl.col("payment").round(0),
|
211 |
+
pl.col("cash_flow").round(0),
|
212 |
+
pl.col("debt").round(0),
|
213 |
+
pl.col("cash_flow_cumulative").round(0),
|
214 |
+
pl.col("flat_price").round(0),
|
215 |
+
pl.col("net_worth").round(0),
|
216 |
+
pl.col("roi").round(2).map_elements(lambda x: f"{x:.0%}")
|
217 |
+
])
|
218 |
+
|
219 |
+
return plot, display_df.to_pandas(), investment_summary
|
220 |
+
|
221 |
+
def create_gradio_interface():
|
222 |
+
with gr.Blocks() as app:
|
223 |
+
gr.Markdown("# Flat Investment Calculator")
|
224 |
+
|
225 |
+
with gr.Row():
|
226 |
+
with gr.Column():
|
227 |
+
flat_price = gr.Number(value=2000000, label="Flat Price (CZK)")
|
228 |
+
one_time_payment = gr.Number(value=100000, label="One Time Payment (CZK)")
|
229 |
+
yearly_interest_rate = gr.Slider(minimum=0, maximum=0.15, value=0.05, step=0.001, label="Yearly Interest Rate")
|
230 |
+
mortgage_years = gr.Slider(minimum=5, maximum=40, value=30, step=1, label="Mortgage Years")
|
231 |
+
with gr.Column():
|
232 |
+
initial_rent = gr.Number(value=12000, label="Initial Monthly Rent (CZK)")
|
233 |
+
yearly_rent_increase = gr.Slider(minimum=0, maximum=0.15, value=0.05, step=0.001, label="Yearly Rent Increase")
|
234 |
+
yearly_flat_appreciation = gr.Slider(minimum=0, maximum=0.15, value=0.07, step=0.001, label="Yearly Flat Appreciation")
|
235 |
+
down_payment_percentage = gr.Slider(minimum=0.1, maximum=1.0, value=0.1, step=0.05, label="Down Payment Percentage")
|
236 |
+
|
237 |
+
calculate_btn = gr.Button("Calculate")
|
238 |
+
|
239 |
+
with gr.Row():
|
240 |
+
investment_summary = gr.Dataframe(
|
241 |
+
headers=["Item", "Amount"],
|
242 |
+
label="Investment Summary",
|
243 |
+
value=[["", ""]], # Initial empty value
|
244 |
+
interactive=False,
|
245 |
+
wrap=True
|
246 |
+
)
|
247 |
+
|
248 |
+
output_plot = gr.Plot()
|
249 |
+
output_table = gr.DataFrame()
|
250 |
+
|
251 |
+
calculate_btn.click(
|
252 |
+
calculate_investment,
|
253 |
+
inputs=[
|
254 |
+
flat_price,
|
255 |
+
one_time_payment,
|
256 |
+
yearly_interest_rate,
|
257 |
+
mortgage_years,
|
258 |
+
initial_rent,
|
259 |
+
yearly_rent_increase,
|
260 |
+
yearly_flat_appreciation,
|
261 |
+
down_payment_percentage
|
262 |
+
],
|
263 |
+
outputs=[output_plot, output_table, investment_summary]
|
264 |
+
)
|
265 |
+
return app
|
266 |
+
|
267 |
+
if __name__ == "__main__":
|
268 |
+
app = create_gradio_interface()
|
269 |
+
app.launch()
|