Spaces:
Runtime error
Runtime error
| """情境模擬模組 - Scenario Simulation Module. | |
| This module handles financial scenario simulations for the urban renewal | |
| project, including delay impacts, cost changes, and interest rate adjustments. | |
| """ | |
| from dataclasses import dataclass | |
| from typing import Any, Optional | |
| import pandas as pd | |
| class ScenarioParams: | |
| """Parameters for scenario simulation.""" | |
| name: str | |
| delay_months: int = 0 | |
| cost_increase_pct: float = 0.0 | |
| rate_adjustment: float = 0.0 | |
| market_price_change: float = 0.0 | |
| class ScenarioResult: | |
| """Results from scenario simulation.""" | |
| scenario_name: str | |
| base_profit: float | |
| adjusted_profit: float | |
| profit_change: float | |
| profit_change_pct: float | |
| delay_cost: float | |
| cost_increase: float | |
| interest_impact: float | |
| market_impact: float | |
| monthly_cashflow: list[float] | |
| irr: float | |
| break_even_month: Optional[int] | |
| def simulate_scenario( | |
| base_income: float, | |
| base_expense: float, | |
| loan_amount: float, | |
| base_rate: float, | |
| monthly_cashflow: list[float], | |
| params: ScenarioParams, | |
| ) -> ScenarioResult: | |
| """Simulate the impact of a scenario on project finances. | |
| Args: | |
| base_income: Base total income. | |
| base_expense: Base total expense. | |
| loan_amount: Total loan amount. | |
| base_rate: Base annual interest rate (%). | |
| monthly_cashflow: List of monthly net cashflow values. | |
| params: Scenario parameters. | |
| Returns: | |
| ScenarioResult: Simulation results. | |
| """ | |
| base_profit = base_income - base_expense | |
| # 1. 延遲成本:每延遲一個月的利息支出 | |
| monthly_interest = loan_amount * (base_rate / 100) / 12 | |
| delay_cost = params.delay_months * monthly_interest | |
| # 2. 成本上漲影響 | |
| cost_increase = base_expense * (params.cost_increase_pct / 100) | |
| # 3. 利率變動影響 (假設貸款期間 36 個月) | |
| new_rate = base_rate + params.rate_adjustment | |
| old_total_interest = loan_amount * (base_rate / 100) * 3 # 3 年 | |
| new_total_interest = loan_amount * (new_rate / 100) * 3 | |
| interest_impact = new_total_interest - old_total_interest | |
| # 4. 市場價格變動 | |
| market_impact = base_income * (params.market_price_change / 100) | |
| # 計算調整後淨利 | |
| adjusted_profit = ( | |
| base_profit | |
| - delay_cost | |
| - cost_increase | |
| - interest_impact | |
| + market_impact | |
| ) | |
| profit_change = adjusted_profit - base_profit | |
| profit_change_pct = (profit_change / base_profit * 100) if base_profit != 0 else 0 | |
| # 調整月度現金流 (模擬延遲影響) | |
| adjusted_cashflow = monthly_cashflow.copy() | |
| if params.delay_months > 0 and len(adjusted_cashflow) > 0: | |
| # 延遲期間仍有利息支出 | |
| for i in range(min(params.delay_months, len(adjusted_cashflow))): | |
| if i < len(adjusted_cashflow): | |
| adjusted_cashflow[i] -= monthly_interest | |
| # 計算損益平衡月份 | |
| cumulative = 0.0 | |
| break_even_month = None | |
| for i, cf in enumerate(adjusted_cashflow): | |
| cumulative += cf | |
| if cumulative > 0 and break_even_month is None: | |
| break_even_month = i + 1 | |
| # Placeholder for IRR calculation | |
| irr_value = 0.0 # This should be calculated based on adjusted_cashflow and initial investment | |
| return ScenarioResult( | |
| scenario_name=params.name, | |
| base_profit=base_profit, | |
| adjusted_profit=adjusted_profit, | |
| profit_change=profit_change, | |
| profit_change_pct=profit_change_pct, | |
| delay_cost=delay_cost, | |
| cost_increase=cost_increase, | |
| interest_impact=interest_impact, | |
| market_impact=market_impact, | |
| monthly_cashflow=adjusted_cashflow, | |
| irr=irr_value, | |
| break_even_month=break_even_month, | |
| ) | |
| def run_sensitivity_analysis( | |
| base_income: float, | |
| base_expense: float, | |
| loan_amount: float, | |
| base_rate: float, | |
| ) -> pd.DataFrame: | |
| """Run sensitivity analysis across multiple scenarios. | |
| Args: | |
| base_income: Base total income. | |
| base_expense: Base total expense. | |
| loan_amount: Total loan amount. | |
| base_rate: Base annual interest rate (%). | |
| Returns: | |
| pd.DataFrame: Sensitivity analysis results. | |
| """ | |
| base_profit = base_income - base_expense | |
| scenarios = [ | |
| # 延遲情境 | |
| ("銷售延遲 3 個月", 3, 0, 0, 0), | |
| ("銷售延遲 6 個月", 6, 0, 0, 0), | |
| ("銷售延遲 12 個月", 12, 0, 0, 0), | |
| # 成本情境 | |
| ("成本上漲 5%", 0, 5, 0, 0), | |
| ("成本上漲 10%", 0, 10, 0, 0), | |
| ("成本上漲 15%", 0, 15, 0, 0), | |
| # 利率情境 | |
| ("利率上升 0.5%", 0, 0, 0.5, 0), | |
| ("利率上升 1.0%", 0, 0, 1.0, 0), | |
| ("利率上升 1.5%", 0, 0, 1.5, 0), | |
| # 市場情境 | |
| ("房價下跌 5%", 0, 0, 0, -5), | |
| ("房價下跌 10%", 0, 0, 0, -10), | |
| # 綜合情境 | |
| ("悲觀情境", 6, 10, 1.0, -5), | |
| ("最悲觀情境", 12, 15, 1.5, -10), | |
| ] | |
| results = [] | |
| for name, delay, cost, rate, market in scenarios: | |
| params = ScenarioParams( | |
| name=name, | |
| delay_months=delay, | |
| cost_increase_pct=cost, | |
| rate_adjustment=rate, | |
| market_price_change=market, | |
| ) | |
| result = simulate_scenario( | |
| base_income, base_expense, loan_amount, base_rate, [], params | |
| ) | |
| results.append({ | |
| "情境": name, | |
| "調整後淨利 (萬)": round(result.adjusted_profit, 0), | |
| "淨利變動 (萬)": round(result.profit_change, 0), | |
| "變動幅度 (%)": round(result.profit_change_pct, 1), | |
| }) | |
| return pd.DataFrame(results) | |
| def calculate_break_even_analysis( | |
| fixed_cost: float, | |
| variable_cost_per_unit: float, | |
| selling_price_per_unit: float, | |
| total_units: int, | |
| ) -> dict[str, Any]: | |
| """Calculate break-even analysis. | |
| Args: | |
| fixed_cost: Total fixed costs. | |
| variable_cost_per_unit: Variable cost per unit. | |
| selling_price_per_unit: Selling price per unit. | |
| total_units: Total sellable units. | |
| Returns: | |
| dict: Break-even analysis results. | |
| """ | |
| contribution_margin = selling_price_per_unit - variable_cost_per_unit | |
| if contribution_margin <= 0: | |
| return { | |
| "break_even_units": None, | |
| "break_even_revenue": None, | |
| "margin_of_safety": 0, | |
| "error": "貢獻邊際為負,無法達到損益平衡", | |
| } | |
| break_even_units = fixed_cost / contribution_margin | |
| break_even_revenue = break_even_units * selling_price_per_unit | |
| total_revenue = total_units * selling_price_per_unit | |
| margin_of_safety = ((total_revenue - break_even_revenue) / total_revenue * 100) if total_revenue > 0 else 0 | |
| return { | |
| "break_even_units": round(break_even_units, 1), | |
| "break_even_revenue": round(break_even_revenue, 0), | |
| "margin_of_safety": round(margin_of_safety, 1), | |
| "contribution_margin": contribution_margin, | |
| } | |
| def calculate_irr_approximation( | |
| initial_investment: float, | |
| monthly_cashflows: list[float], | |
| ) -> Optional[float]: | |
| """Calculate approximate IRR using Newton-Raphson method. | |
| Args: | |
| initial_investment: Initial investment (negative value). | |
| monthly_cashflows: List of monthly cash flows. | |
| Returns: | |
| float: Approximate IRR as annual percentage, or None if not converging. | |
| """ | |
| if not monthly_cashflows: | |
| return None | |
| # 將月度現金流轉換為年度 | |
| annual_cashflows = [] | |
| for i in range(0, len(monthly_cashflows), 12): | |
| annual_cashflows.append(sum(monthly_cashflows[i : i + 12])) | |
| # 加入初始投資 | |
| cashflows = [-initial_investment] + annual_cashflows | |
| # Newton-Raphson 迭代 | |
| rate = 0.1 # 初始猜測 10% | |
| for _ in range(100): # 最多 100 次迭代 | |
| npv = sum(cf / (1 + rate) ** i for i, cf in enumerate(cashflows)) | |
| npv_derivative = sum( | |
| -i * cf / (1 + rate) ** (i + 1) for i, cf in enumerate(cashflows) | |
| ) | |
| if abs(npv_derivative) < 1e-10: | |
| break | |
| new_rate = rate - npv / npv_derivative | |
| if abs(new_rate - rate) < 1e-6: | |
| return round(new_rate * 100, 2) | |
| rate = new_rate | |
| return round(rate * 100, 2) if -1 < rate < 10 else None | |