用 Python 快速追踪基金的收益情况!
那天晚上十一点多,我在客厅沙发上刷手机,看着基金 App 上一堆红红绿绿的曲线,心里就一个感觉:乱。 单只基金涨跌能看到,但我每个月定投、又在不同平台买,整个组合到底赚了多少、哪只拖后腿,完全没有全局观。
后来我干脆想,那不如用 Python 自己搞一套最简单的“收益仪表盘”,每天一跑,一目了然。今天就把这个东西摊开跟你聊聊,顺便把代码也贴出来,你照着改一下就能用。
我当时是直接在电脑上建了个 fund_positions.csv,放在项目目录里,大概这样:
code,name,shares,cost_price
161725,白酒指数,1000,1.23
501018,新能源车,800,1.80
001500,消费精选,1500,0.95
code:基金代码name:你自己方便看的名字shares:当前持有份额cost_price:你的平均持仓成本(可以自己在 App 里看个大概填上)
先别纠结“多笔买入成本怎么精确计算”这种问题,等脚本跑起来你再慢慢抠,这样不容易半途而废。
我一般习惯先写一个特别土但能跑的版本,完全不管优不优雅。比如我们先不连任何接口,假装“当前净值”帮你手动填在代码里,先把计算打通。
import pandas as pd# 1. 读持仓表
df = pd.read_csv("fund_positions.csv")
# 2. 暂时手动写当前净值(后面再换成接口)
current_nav_map = {
"161725": 1.45,
"501018": 1.60,
"001500": 1.10
}
# 3. 把当前净值映射到 DataFrame 里
df["current_nav"] = df["code"].map(current_nav_map)
# 4. 计算每只基金的数据
df["cost_amount"] = df["shares"] * df["cost_price"] # 成本金额
df["current_amount"] = df["shares"] * df["current_nav"] # 当前市值
df["profit"] = df["current_amount"] - df["cost_amount"] # 浮动盈亏
df["profit_rate"] = df["profit"] / df["cost_amount"] # 收益率
# 5. 打印看看效果
pd.set_option("display.float_format", lambda x: f"{x:,.2f}")
print(df[["code", "name", "cost_amount", "current_amount", "profit", "profit_rate"]])
# 6. 汇总一下整体
total_cost = df["cost_amount"].sum()
total_value = df["current_amount"].sum()
total_profit = total_value - total_cost
total_rate = total_profit / total_cost
print("\n===== 组合汇总 =====")
print(f"总成本: {total_cost:,.2f}")
print(f"总市值: {total_value:,.2f}")
print(f"总盈亏: {total_profit:,.2f}")
print(f"整体收益率: {total_rate:.2%}")
这个版本虽然“假装”当前净值,但是你一跑,就能立刻看到类似这样的输出(数值我随便举个例子):
code name cost_amount current_amount profit profit_rate
0 161725 白酒指数 1,230.00 1,450.00 220.00 17.89%
1 501018 新能源车 1,440.00 1,280.00 -160.00 -11.11%
2 001500 消费精选 1,425.00 1,650.00 225.00 15.79%===== 组合汇总 =====
总成本: 4,095.00
总市值: 4,380.00
总盈亏: 285.00
整体收益率: 6.96%
这一步的意义是:把“算收益”这件事拆成了几个很清楚的字段,逻辑完全透明。
上面那种手动填净值的方式,只适合你先验证公式。真正使用肯定得自动拉数据,不然你天天手抄净值会崩溃。
数据来源有很多种:
你自己每天从平台导出 CSV(有些 App 支持导出交易/持仓明细) 用第三方库(比如某些基金/股票接口库) 直接调公开 API(有的基金网站会有 http 接口)
这里我不深入哪家平台接口,用一个“占位函数”的方式写给你,你只要把那一小段替换掉就行,其他逻辑不用动。
比如我们先写一个这样的函数:
import requestsdefget_fund_nav(code: str) -> float:
"""
通过基金代码获取最新净值。
这里写的是伪代码,你根据自己选的接口改一下就行。
"""
# 举个结构示意,url 换成你实际的
url = f"https://example.com/fund/nav?code={code}"
resp = requests.get(url, timeout=5)
resp.raise_for_status()
data = resp.json()
# 假设返回结构是 {"code": "...", "nav": 1.2345, "date": "2024-01-01"}
return float(data["nav"])
然后把第二步里的 current_nav_map 换掉:
# 2. 根据 code 批量去拿当前净值
deffetch_all_current_nav(df: pd.DataFrame) -> pd.Series:
navs = {}
for code in df["code"]:
try:
navs[code] = get_fund_nav(code)
except Exception as e:
print(f"获取 {code} 净值失败:{e}")
# 可以选择:用成本价顶一下,或者用上一次的净值缓存
navs[code] = None
return pd.Series(navs)df["current_nav"] = df["code"].map(fetch_all_current_nav(df))
这样你的主流程还是原来的那套,只是“当前净值”这列换成了自动获取。 你先可以只对一两只基金试,然后再慢慢扩展。
我那个脚本跑顺之后,强迫症就犯了,总觉得只看一行数字不过瘾,想看过去一段时间的走势。 最暴力的方法就是:每天把计算结果存一行到一个历史文件里,比如 fund_history.csv。
先改一下脚本最后部分,加个“落盘”的动作:
from datetime import datetime
import osdefappend_summary_history(total_cost, total_value, total_profit, total_rate):
today = datetime.now().strftime("%Y-%m-%d")
row = {
"date": today,
"total_cost": total_cost,
"total_value": total_value,
"total_profit": total_profit,
"total_rate": total_rate,
}
history_file = "fund_history.csv"
if os.path.exists(history_file):
hist_df = pd.read_csv(history_file)
# 同一天多次运行可以选择覆盖或者追加,这里简单粗暴去重
hist_df = hist_df[hist_df["date"] != today]
hist_df = pd.concat([hist_df, pd.DataFrame([row])], ignore_index=True)
else:
hist_df = pd.DataFrame([row])
hist_df.to_csv(history_file, index=False)
print("已写入历史记录:", row)
# 在打印汇总之后,加一句:
append_summary_history(total_cost, total_value, total_profit, total_rate)
跑几天之后,你就会有一个 fund_history.csv,类似这样:
date,total_cost,total_value,total_profit,total_rate
2024-01-01,10000,10300,300,0.03
2024-01-02,10000,10250,250,0.025
2024-01-03,10000,10420,420,0.042
...
然后用 matplotlib 画一下收益率曲线(不用搞得多花哨,能看就行):
import pandas as pd
import matplotlib.pyplot as plthist_df = pd.read_csv("fund_history.csv", parse_dates=["date"])
hist_df = hist_df.sort_values("date")
plt.figure(figsize=(10, 4))
plt.plot(hist_df["date"], hist_df["total_rate"])
plt.title("组合整体收益率走势")
plt.xlabel("日期")
plt.ylabel("收益率")
plt.grid(True)
plt.tight_layout()
plt.show()
你把这个图一打开,心里大概就有数了:是最近走势差一点,还是长期趋势在往上。
我后来嫌每次还要 python xxx.py 麻烦,就干脆把主流程封进一个 main(),脚本放到某个固定目录,然后用系统自带的定时任务搞一下:
脚本大概这么收个尾:
defmain():
df = pd.read_csv("fund_positions.csv")
# 这里接上前面那一整套处理逻辑:
# 1)获取 current_nav
# 2)计算 cost_amount / current_amount / profit / profit_rate
# 3)打印单只基金和汇总
# 4)append_summary_historyif __name__ == "__main__":
main()
Windows 可以用“任务计划程序”每天 21:00 跑一次 mac / Linux 就用 crontab,一行:
0 21 * * * /usr/bin/python3 /path/to/your/fund_tracker.py >> /path/to/log.txt 2>&1
这样你每天打开 fund_history.csv 或者那张收益曲线图,就知道今天的组合情况了,完全不用再一只只点开 App 盯着看。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB