前言

“定投真的比一次性投资好吗?”——这是投资领域最经典的问题之一。

本文用Python模拟上证指数近5年的定投策略,通过IRR(内部收益率)方法计算真实年化收益率,用数据回答这个问题。


技术栈

工具 用途
tushare 获取A股指数数据
pandas 数据处理与重采样
matplotlib 可视化
numpy_financial IRR计算

Step 1:获取上证指数数据

import tushare as ts
import pandas as pd
import datetime

# 设置tushare token
ts.set_token('your_token_here')
pro = ts.pro_api()

# 获取近5年日期范围
end_date = datetime.datetime.today()
start_date = end_date - datetime.timedelta(days=365*5)

# 获取上证指数日线数据
sh_index = pro.index_daily(
    ts_code='000001.SH',
    start_date=start_date.strftime('%Y%m%d'),
    end_date=end_date.strftime('%Y%m%d')
)

# 转换为月末收盘价
sh_index['trade_date'] = pd.to_datetime(sh_index['trade_date'])
sh_index = sh_index.sort_values('trade_date').set_index('trade_date')
df_month = sh_index.resample('ME').last().reset_index()
df_month = df_month[['trade_date', 'close']]

踩坑提醒resample('M')在新版pandas中已废弃,需改用resample('ME')(Month End)。


Step 2:可视化走势

import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['SimSun']  # 中文字体
matplotlib.rcParams['axes.unicode_minus'] = False

plt.figure(figsize=(12, 6))
plt.plot(df_month['trade_date'], df_month['close'], marker='o')
plt.title('上证指数近5年月末收盘价')
plt.xlabel('日期')
plt.ylabel('收盘价')
plt.grid(True)
plt.show()

近5年上证指数在3000-3700点区间震荡,典型的”心电图”行情。


Step 3:模拟定投策略

假设每月月末定投1000元:

monthly_invest = 1000  # 每月定投金额

# 每月买入份额 = 投资金额 / 当月收盘价
df_month['share'] = monthly_invest / df_month['close']

# 累计份额和累计投入
df_month['cum_share'] = df_month['share'].cumsum()
df_month['cum_invest'] = (monthly_invest).cumsum()  # 等价于 monthly_invest * 期数

# 当前总市值 = 累计份额 × 最新收盘价
current_value = df_month['cum_share'].iloc[-1] * df_month['close'].iloc[-1]

print(f"累计投入: {df_month['cum_invest'].iloc[-1]:.2f}")
print(f"当前总市值: {current_value:.2f}")

输出

累计投入: 61000.00 元
当前总市值: 65098.80 元

总收益 = 65098.80 - 61000 = 4098.80 元(约6.7%总收益率)


Step 4:用IRR计算真实年化收益率

总收益率6.7%看起来不错,但这是5年累计的。真实年化收益率需要用IRR(内部收益率)计算。

为什么不能简单除以年数? 因为定投是分期投入的,每笔资金的持有时间不同。IRR考虑了每笔现金流的时间价值。

from numpy_financial import irr

# 现金流:每月投入-1000元,最后一期加上总市值
cash_flows = [-monthly_invest] * len(df_month)
cash_flows[-1] += current_value  # 最后一期:-1000 + 总市值

# 计算月度IRR,转换为年化
ytm_monthly = irr(cash_flows)
ytm_annual = (1 + ytm_monthly) ** 12 - 1

print(f"定投年化收益率(YTM):{ytm_annual*100:.2f}%")

输出

定投年化收益率(YTM):2.61%

Step 5:对比一次性投资

# 一次性投资:第一月投入全部资金
lump_sum_invest = df_month['cum_invest'].iloc[-1]  # 61000元
lump_sum_cash_flows = [-lump_sum_invest] + [0] * (len(df_month) - 1)
lump_sum_cash_flows[-1] += current_value

ytm_lump_monthly = irr(lump_sum_cash_flows)
ytm_lump_annual = (1 + ytm_lump_monthly) ** 12 - 1

print(f"一次性投资年化收益率(YTM):{ytm_lump_annual*100:.2f}%")

输出

定投年化收益率(YTM):2.61%
一次性投资年化收益率(YTM):1.31%

结果分析

策略 总投入 最终市值 总收益率 年化收益率(YTM)
每月定投1000元 61,000元 65,098.80元 6.72% 2.61%
一次性投入61,000元 61,000元 65,098.80元 6.72% 1.31%

关键发现

  1. 两种策略的总收益率相同(都是6.72%),因为最终都持有相同金额
  2. 定投的年化收益率更高(2.61% vs 1.31%),因为资金是分期投入的,后投入的资金持有时间更短
  3. 在震荡市中,定投通过”低点多买、高点少买”降低了平均成本

IRR方法详解

IRR是金融学中计算投资收益率的标准方法,考虑了现金流的时间价值。

核心思想:找到一个折现率r,使得所有现金流的净现值(NPV)等于0。

\[NPV = \sum_{t=0}^{n} \frac{CF_t}{(1+r)^t} = 0\]

对于定投:

  • 每月现金流:CF = -1000(投入)
  • 最后一期:CF = -1000 + 总市值(投入 + 赎回)
# IRR的计算过程
# cash_flows = [-1000, -1000, -1000, ..., -1000 + 65098.80]
# irr(cash_flows) = 月度收益率
# 年化 = (1 + 月度收益率)^12 - 1

完整代码

import tushare as ts
import pandas as pd
import datetime
import matplotlib.pyplot as plt
from numpy_financial import irr

# 1. 获取数据
ts.set_token('your_token_here')
pro = ts.pro_api()
end_date = datetime.datetime.today()
start_date = end_date - datetime.timedelta(days=365*5)
sh_index = pro.index_daily(ts_code='000001.SH',
                           start_date=start_date.strftime('%Y%m%d'),
                           end_date=end_date.strftime('%Y%m%d'))
sh_index['trade_date'] = pd.to_datetime(sh_index['trade_date'])
sh_index = sh_index.sort_values('trade_date').set_index('trade_date')
df_month = sh_index.resample('ME').last().reset_index()[['trade_date', 'close']]

# 2. 模拟定投
monthly_invest = 1000
df_month['share'] = monthly_invest / df_month['close']
df_month['cum_share'] = df_month['share'].cumsum()
current_value = df_month['cum_share'].iloc[-1] * df_month['close'].iloc[-1]

# 3. 计算定投YTM
cash_flows = [-monthly_invest] * len(df_month)
cash_flows[-1] += current_value
ytm_annual = (1 + irr(cash_flows)) ** 12 - 1

# 4. 计算一次性投资YTM
lump_sum = monthly_invest * len(df_month)
lump_flows = [-lump_sum] + [0] * (len(df_month) - 1)
lump_flows[-1] += current_value
ytm_lump_annual = (1 + irr(lump_flows)) ** 12 - 1

print(f"定投年化收益率:{ytm_annual*100:.2f}%")
print(f"一次性投资年化收益率:{ytm_lump_annual*100:.2f}%")

总结

指标 定投 一次性投资
年化收益率 2.61% 1.31%
资金利用率 分期投入 全部投入
风险分散 ✅ 自动平滑 ❌ 集中暴露
适合场景 震荡市 牛市初期

投资建议:在A股这种长期震荡的市场中,定投是更稳健的策略。虽然牛市中一次性投资可能收益更高,但定投的”纪律性”和”平滑成本”特性更适合普通投资者。


Notebook文件:F:\202507\sh_index_investment_analysis.ipynb,216KB完整分析。

Stay tuned! 🚀