File size: 5,704 Bytes
9c02682 a0d4a3a 9c02682 28aa0af a0d4a3a b168a2d a0d4a3a b168a2d a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 5801ff5 9c02682 5801ff5 a0d4a3a 5801ff5 9c02682 a0d4a3a 5801ff5 9c02682 5801ff5 9c02682 5801ff5 9c02682 5801ff5 a0d4a3a 5801ff5 a0d4a3a b168a2d 9c02682 28aa0af b168a2d a0d4a3a 9c02682 b168a2d 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 9c02682 a0d4a3a 5801ff5 b168a2d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
import gradio as gr # UI 框架 :contentReference[oaicite:10]{index=10}
import pandas as pd # 表格处理 :contentReference[oaicite:11]{index=11}
import numpy as np
import plotly.graph_objects as go # 用于瀑布图和折线图 :contentReference[oaicite:12]{index=12}
def calculate_schedule(
principal: float,
deposit: float,
annual_rate: float,
compounding: str,
deposit_freq: str,
inflation_rate: float,
inflation_freq: str,
display_freq: str,
years: int,
stop_deposit_year: int
):
"""
- compounding: 复利频率 (Annual/Monthly/Daily)
- deposit_freq: 定投频率 (Annual/Monthly/Daily)
- inflation_rate & inflation_freq: 定投膨胀率与频率
- stop_deposit_year: 第几年后停止定投
- display_freq: 结果展示频率 (Yearly/Monthly/Daily)
"""
# 频率映射
freq_map = {"Annual": 1, "Monthly": 12, "Daily": 365}
m_comp = freq_map[compounding]
m_dep = freq_map[deposit_freq]
m_inf = freq_map[inflation_freq]
total_days = years * 365
comp_interval = 365 // m_comp
dep_interval = 365 // m_dep
stop_day = stop_deposit_year * 365
balance = principal
total_invested = principal
deposit_count = 0
schedule = []
for day in range(1, total_days + 1):
# 复利增长:每日或周期性触发 :contentReference[oaicite:13]{index=13}
if compounding == "Daily":
balance *= 1 + (annual_rate / 100) / 365
elif day % comp_interval == 0:
balance *= 1 + (annual_rate / 100) / m_comp
# 定投执行:仅在 stop_day 之前且周期点加入膨胀后金额 :contentReference[oaicite:14]{index=14}
if day <= stop_day and day % dep_interval == 0:
deposit_count += 1
amount = deposit * (1 + (inflation_rate/100)/m_inf) ** (deposit_count - 1)
balance += amount
total_invested += amount
# 根据展示粒度记录
record = False
if display_freq == "Daily":
record = True
period = day
elif display_freq == "Monthly" and day % (365 // 12) == 0:
record = True
period = day // (365 // 12)
elif display_freq == "Yearly" and day % 365 == 0:
record = True
period = day // 365
if record:
fv = balance
schedule.append({
display_freq: period,
"Future Value (RMB)": fv,
"Total Invested (RMB)": total_invested,
"Interest Earned (RMB)": fv - total_invested,
})
# 构造 DataFrame 并保留两位小数 :contentReference[oaicite:15]{index=15}
df = pd.DataFrame(schedule).round(2)
df["Interest Increment (RMB)"] = df["Interest Earned (RMB)"] \
.diff().fillna(0).round(2)
# 折线图:Future Value :contentReference[oaicite:16]{index=16}
fig_line = go.Figure(go.Scatter(
x=df[display_freq],
y=df["Future Value (RMB)"],
mode='lines+markers',
name='Future Value'
))
fig_line.update_layout(
title=f"Compound Growth over Time ({display_freq})",
xaxis_title=display_freq,
yaxis_title="Future Value (RMB)",
template="plotly_white" # 专业配色 :contentReference[oaicite:17]{index=17}
)
# 瀑布图:Interest Increment :contentReference[oaicite:18]{index=18}
fig_waterfall = go.Figure(go.Waterfall(
x=df[display_freq],
y=df["Interest Increment (RMB)"],
measure=["relative"] * len(df),
name="Interest Increment"
))
fig_waterfall.update_layout(
title=f"Interest Increment per {display_freq}",
xaxis_title=display_freq,
yaxis_title="Interest Increment (RMB)",
template="plotly_white"
)
return df, fig_line, fig_waterfall
with gr.Blocks() as demo:
gr.Markdown("## 复利计算器\n填写参数后点击“计算”查看结果")
with gr.Row():
principal = gr.Number(label="初始本金 (RMB)", value=20000)
deposit = gr.Number(label="每次定投 (RMB)", value=5000)
annual_rate = gr.Number(label="年化收益率 (%)", value=10.22)
with gr.Row():
compounding = gr.Radio(choices=["Annual","Monthly","Daily"], label="复利频率", value="Monthly")
deposit_freq = gr.Radio(choices=["Annual","Monthly","Daily"], label="定投频率", value="Monthly")
inflation_rate = gr.Number(label="定投膨胀率 (%)", value=0.0)
inflation_freq = gr.Radio(choices=["Annual","Monthly","Daily"], label="膨胀频率", value="Annual")
with gr.Row():
display_freq = gr.Radio(choices=["Yearly","Monthly","Daily"], label="结果展示频率", value="Yearly")
years = gr.Slider(1, 50, value=41, label="计算年限 (年)")
stop_deposit_year = gr.Slider(0, 50, value=41, label="何年后停止定投 (年)")
compute_btn = gr.Button("计算", variant="primary") # 按钮触发 :contentReference[oaicite:19]{index=19}
result_table = gr.Dataframe(interactive=False)
result_plot = gr.Plot()
interest_plot = gr.Plot()
compute_btn.click(
fn=calculate_schedule,
inputs=[
principal, deposit, annual_rate,
compounding, deposit_freq,
inflation_rate, inflation_freq,
display_freq, years, stop_deposit_year
],
outputs=[result_table, result_plot, interest_plot]
)
demo.launch()
|