Spaces:
Sleeping
Sleeping
feat: minor cosmetic changes
Browse files
app.py
CHANGED
|
@@ -14,10 +14,16 @@ except ImportError:
|
|
| 14 |
rate -= npv / d_npv if d_npv else 0
|
| 15 |
return rate
|
| 16 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 17 |
# ========== Core Model ==========
|
| 18 |
def build_property_projection(purchase_price, equity, loan_years, annual_interest_rate,
|
| 19 |
-
monthly_payment, monthly_rent, vacancy_months,
|
| 20 |
-
inflation_rate):
|
| 21 |
loan_balance = purchase_price - equity
|
| 22 |
monthly_interest = annual_interest_rate / 12
|
| 23 |
schedule = []
|
|
@@ -77,33 +83,17 @@ def compute_etf_irr(principal, years, annual_return):
|
|
| 77 |
cashflows = [-principal] + [0] * (years - 1) + [round(final_value)]
|
| 78 |
return irr(cashflows)
|
| 79 |
|
| 80 |
-
def build_hybrid_projection(df_property, annual_return):
|
| 81 |
-
etf_balance = 0
|
| 82 |
-
projection = []
|
| 83 |
-
for _, row in df_property.iterrows():
|
| 84 |
-
etf_balance *= (1 + annual_return)
|
| 85 |
-
etf_balance += row["NetCashFlow"]
|
| 86 |
-
projection.append({"Year": row["Year"], "ETF_Balance": etf_balance})
|
| 87 |
-
return pd.DataFrame(projection)
|
| 88 |
-
|
| 89 |
-
def compute_hybrid_irr(df_property, equity, df_hybrid, sell_property_at_end=True):
|
| 90 |
-
cashflows = [-equity]
|
| 91 |
-
for i, row in df_property.iterrows():
|
| 92 |
-
cf = 0
|
| 93 |
-
if i == len(df_property) - 1 and sell_property_at_end:
|
| 94 |
-
cf += row["PropertyValue"] - row["LoanBalance"]
|
| 95 |
-
cf += df_hybrid.iloc[-1]["ETF_Balance"]
|
| 96 |
-
else:
|
| 97 |
-
cf += row["NetCashFlow"]
|
| 98 |
-
cashflows.append(cf)
|
| 99 |
-
return irr(cashflows)
|
| 100 |
-
|
| 101 |
# ========== Gradio App ==========
|
| 102 |
def simulate(
|
| 103 |
-
purchase_price, equity, loan_years,
|
| 104 |
monthly_payment, monthly_rent, vacancy_months, annual_renovation,
|
| 105 |
-
|
| 106 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 107 |
# Property
|
| 108 |
df_property = build_property_projection(purchase_price, equity, loan_years,
|
| 109 |
annual_interest_rate, monthly_payment,
|
|
@@ -115,24 +105,27 @@ def simulate(
|
|
| 115 |
df_etf = build_etf_projection(equity, loan_years, etf_annual_return)
|
| 116 |
etf_irr = compute_etf_irr(equity, loan_years, etf_annual_return)
|
| 117 |
|
| 118 |
-
# Hybrid
|
| 119 |
-
df_hybrid = build_hybrid_projection(df_property, etf_annual_return)
|
| 120 |
-
hybrid_irr = compute_hybrid_irr(df_property, equity, df_hybrid)
|
| 121 |
-
|
| 122 |
# Results summary
|
| 123 |
summary = f"""
|
| 124 |
π **Results after {loan_years} years**:
|
| 125 |
-
- Property IRR: {prop_irr
|
| 126 |
-
- ETF IRR: {etf_irr
|
| 127 |
-
- Hybrid IRR (Property + ETF cashflows): {hybrid_irr*100:.2f}%
|
| 128 |
|
| 129 |
π° Final Values:
|
| 130 |
-
- Property Net Worth: {df_property.iloc[-1]['NetWorth']
|
| 131 |
-
- ETF Value: {df_etf.iloc[-1]['ETF_Value']
|
| 132 |
-
- Hybrid Value (Property + ETF): {df_property.iloc[-1]['NetWorth'] + df_hybrid.iloc[-1]['ETF_Balance']:,} HUF
|
| 133 |
"""
|
| 134 |
|
| 135 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
|
| 137 |
with gr.Blocks(title="Investment Simulator") as demo:
|
| 138 |
gr.Markdown("# π vs π Investment Simulator")
|
|
@@ -140,24 +133,23 @@ with gr.Blocks(title="Investment Simulator") as demo:
|
|
| 140 |
|
| 141 |
with gr.Row():
|
| 142 |
with gr.Column():
|
| 143 |
-
purchase_price = gr.Number(value=60_000_000, label="Purchase Price (HUF)")
|
| 144 |
-
equity = gr.Number(value=20_000_000, label="Equity (HUF)")
|
| 145 |
-
loan_years = gr.Number(value=20, label="Loan Term (years)")
|
| 146 |
-
annual_interest_rate = gr.Number(value=0
|
| 147 |
-
monthly_payment = gr.Number(value=220_000, label="Monthly Loan Payment (HUF)")
|
| 148 |
-
monthly_rent = gr.Number(value=170_000, label="Monthly Rent (HUF)")
|
| 149 |
-
vacancy_months = gr.Number(value=1, label="Vacancy (months/year)")
|
| 150 |
-
annual_renovation = gr.Number(value=100_000, label="Annual Renovation (HUF)")
|
| 151 |
-
inflation_rate = gr.Number(value=
|
| 152 |
-
etf_annual_return = gr.Number(value=0
|
| 153 |
|
| 154 |
run_button = gr.Button("Run Simulation")
|
| 155 |
|
| 156 |
with gr.Column():
|
| 157 |
results = gr.Markdown()
|
| 158 |
-
df_property_out = gr.Dataframe(interactive=False)
|
| 159 |
-
df_etf_out = gr.Dataframe(interactive=False)
|
| 160 |
-
df_hybrid_out = gr.Dataframe(interactive=False)
|
| 161 |
|
| 162 |
run_button.click(
|
| 163 |
simulate,
|
|
@@ -166,7 +158,7 @@ with gr.Blocks(title="Investment Simulator") as demo:
|
|
| 166 |
monthly_payment, monthly_rent, vacancy_months, annual_renovation,
|
| 167 |
inflation_rate, etf_annual_return
|
| 168 |
],
|
| 169 |
-
outputs=[results, df_property_out, df_etf_out
|
| 170 |
)
|
| 171 |
|
| 172 |
if __name__ == "__main__":
|
|
|
|
| 14 |
rate -= npv / d_npv if d_npv else 0
|
| 15 |
return rate
|
| 16 |
|
| 17 |
+
def format_huf(x):
|
| 18 |
+
return f"{x:,.0f} HUF".replace(",", " ")
|
| 19 |
+
|
| 20 |
+
def format_pct(x):
|
| 21 |
+
return f"{x*100:.2f}%"
|
| 22 |
+
|
| 23 |
# ========== Core Model ==========
|
| 24 |
def build_property_projection(purchase_price, equity, loan_years, annual_interest_rate,
|
| 25 |
+
monthly_payment, monthly_rent, vacancy_months,
|
| 26 |
+
annual_renovation, inflation_rate):
|
| 27 |
loan_balance = purchase_price - equity
|
| 28 |
monthly_interest = annual_interest_rate / 12
|
| 29 |
schedule = []
|
|
|
|
| 83 |
cashflows = [-principal] + [0] * (years - 1) + [round(final_value)]
|
| 84 |
return irr(cashflows)
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
# ========== Gradio App ==========
|
| 87 |
def simulate(
|
| 88 |
+
purchase_price, equity, loan_years, annual_interest_rate_pct,
|
| 89 |
monthly_payment, monthly_rent, vacancy_months, annual_renovation,
|
| 90 |
+
inflation_rate_pct, etf_annual_return_pct
|
| 91 |
):
|
| 92 |
+
# Convert % to decimals
|
| 93 |
+
annual_interest_rate = annual_interest_rate_pct / 100
|
| 94 |
+
inflation_rate = inflation_rate_pct / 100
|
| 95 |
+
etf_annual_return = etf_annual_return_pct / 100
|
| 96 |
+
|
| 97 |
# Property
|
| 98 |
df_property = build_property_projection(purchase_price, equity, loan_years,
|
| 99 |
annual_interest_rate, monthly_payment,
|
|
|
|
| 105 |
df_etf = build_etf_projection(equity, loan_years, etf_annual_return)
|
| 106 |
etf_irr = compute_etf_irr(equity, loan_years, etf_annual_return)
|
| 107 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
# Results summary
|
| 109 |
summary = f"""
|
| 110 |
π **Results after {loan_years} years**:
|
| 111 |
+
- Property IRR: {format_pct(prop_irr)}
|
| 112 |
+
- ETF IRR: {format_pct(etf_irr)}
|
|
|
|
| 113 |
|
| 114 |
π° Final Values:
|
| 115 |
+
- Property Net Worth: {format_huf(df_property.iloc[-1]['NetWorth'])}
|
| 116 |
+
- ETF Value: {format_huf(df_etf.iloc[-1]['ETF_Value'])}
|
|
|
|
| 117 |
"""
|
| 118 |
|
| 119 |
+
# Format tables for readability
|
| 120 |
+
df_property_fmt = df_property.copy()
|
| 121 |
+
for col in ["InterestPaid", "PrincipalPaid", "LoanBalance",
|
| 122 |
+
"RentIncome", "NetCashFlow", "PropertyValue", "NetWorth"]:
|
| 123 |
+
df_property_fmt[col] = df_property_fmt[col].apply(lambda x: format_huf(x))
|
| 124 |
+
|
| 125 |
+
df_etf_fmt = df_etf.copy()
|
| 126 |
+
df_etf_fmt["ETF_Value"] = df_etf_fmt["ETF_Value"].apply(lambda x: format_huf(x))
|
| 127 |
+
|
| 128 |
+
return summary, df_property_fmt, df_etf_fmt
|
| 129 |
|
| 130 |
with gr.Blocks(title="Investment Simulator") as demo:
|
| 131 |
gr.Markdown("# π vs π Investment Simulator")
|
|
|
|
| 133 |
|
| 134 |
with gr.Row():
|
| 135 |
with gr.Column():
|
| 136 |
+
purchase_price = gr.Number(value=60_000_000, label="Purchase Price (HUF)", precision=0)
|
| 137 |
+
equity = gr.Number(value=20_000_000, label="Equity (HUF)", precision=0)
|
| 138 |
+
loan_years = gr.Number(value=20, label="Loan Term (years)", precision=0)
|
| 139 |
+
annual_interest_rate = gr.Number(value=3.0, label="Loan Interest (THM %)", precision=2)
|
| 140 |
+
monthly_payment = gr.Number(value=220_000, label="Monthly Loan Payment (HUF)", precision=0)
|
| 141 |
+
monthly_rent = gr.Number(value=170_000, label="Monthly Rent (HUF)", precision=0)
|
| 142 |
+
vacancy_months = gr.Number(value=1, label="Vacancy (months/year)", precision=0)
|
| 143 |
+
annual_renovation = gr.Number(value=100_000, label="Annual Renovation (HUF)", precision=0)
|
| 144 |
+
inflation_rate = gr.Number(value=4.5, label="Inflation Rate (%)", precision=2)
|
| 145 |
+
etf_annual_return = gr.Number(value=11.0, label="ETF Annual Return (%)", precision=2)
|
| 146 |
|
| 147 |
run_button = gr.Button("Run Simulation")
|
| 148 |
|
| 149 |
with gr.Column():
|
| 150 |
results = gr.Markdown()
|
| 151 |
+
df_property_out = gr.Dataframe(interactive=False, wrap=True)
|
| 152 |
+
df_etf_out = gr.Dataframe(interactive=False, wrap=True)
|
|
|
|
| 153 |
|
| 154 |
run_button.click(
|
| 155 |
simulate,
|
|
|
|
| 158 |
monthly_payment, monthly_rent, vacancy_months, annual_renovation,
|
| 159 |
inflation_rate, etf_annual_return
|
| 160 |
],
|
| 161 |
+
outputs=[results, df_property_out, df_etf_out]
|
| 162 |
)
|
| 163 |
|
| 164 |
if __name__ == "__main__":
|