财富仪表盘-ghostfolio
步骤/目录:
1.背景介绍
2.docker安装ghostfolio
3.ghostfolio的基本使用与配置
4.总结本文首发于个人博客https://lisper517.top/index.php/archives/4667/,转载请注明出处。
本文的目的是使用ghostfolio搭建财富管理网页,直观展示个人或者家庭名下的存款、ETF、股票、期货、加密货币等资产。特别注意:不好用、没有什么实用价值,只是弄着玩。建议自行搭建前先去 官方站 建个账号试一下。
本文写作日期为2026年10月2日,在Ubuntu 22.04.5LTS系统演示。特别注意:笔者在1核/2核、2GB内存的云服务器上运行ghostfolio还是比较吃力的,死机了三四次也无法部署,有条件的建议用更好的服务器配置。
本文使用到的参考网页有:
(1)github-ghostfolio
(2)开源财富管理软件Ghostfolio的安装
1.背景介绍
笔者之前一直使用一张excel表格记录每一笔买入、卖出、分红(见 投资策略-定投 ),最近突发奇想要搭建一个网页仪表盘展示名下资产,问了AI得到ghostfolio这个工具。除了ghostfolio,还可选择
(1)Wealthfolio,可以搜到国内股票与场内基金,但搜不到场外基金,有桌面版。
(2)Capitrack,需自己搭建,未尝试。
(3)基金记账簿(fund-ledger),一款国内app。
(4)Fund Helper,国内开源,可自己部署,有 演示网页 。
但是要注意可能有泄露风险,不论开源/闭源、免费/收费,但资金少应该问题不大。
2.docker安装ghostfolio
如果无法安装docker或者使用docker官方仓库,可能是被封禁了,解决方法请自行搜索,或者参考 自建网盘-nextcloud 。
首先生成3个密码、1个密钥并保存,等下要用:
(运行3次)openssl rand -hex 16
openssl rand -base64 32然后安装ghostfolio
mkdir -p /docker/ghostfolio/data/ghostfolio
mkdir -p /docker/ghostfolio/data/postgres
mkdir -p /docker/ghostfolio/data/redis
cd /docker/ghostfolio
vim .env写入如下内容( 官方参考 ):
COMPOSE_PROJECT_NAME=ghostfolio
# CACHE
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=redis密码 # 改成刚才生成的密码之一
# POSTGRES
POSTGRES_DB=ghostfolio-db
POSTGRES_USER=user
POSTGRES_PASSWORD=postgres密码 # 改成刚才生成的密码之二
# VARIOUS
ACCESS_TOKEN_SALT=密码三 # 改成刚才生成的密码之三
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@gf-postgres:5432/${POSTGRES_DB}?connect_timeout=300
JWT_SECRET_KEY=密钥 # 改成刚才生成的密钥注意:
DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@gf-postgres:5432/${POSTGRES_DB}?其中的 gf-postgres 要写成下面yml文件中的postgres容器名。
然后写ghostfolio的yml文件:
(查询当前用户的uid、gid)id $user
vim docker-compose.yml写入如下内容( 官方参考 )。由于笔者使用了NPM(nginx proxy manager),这里的yml稍有不同、改动处已做了注释,详见 可视化管理Nginx-Nginx Proxy Manager
name: ghostfolio
services:
ghostfolio:
image: docker.io/ghostfolio/ghostfolio:latest
container_name: ghostfolio
restart: unless-stopped
init: true
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
env_file:
- ./.env
#ports: # 若未使用NPM则解除注释
#- 3333:3333
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ['CMD-SHELL', 'curl -f http://localhost:3333/api/v1/health']
interval: 10s
timeout: 5s
retries: 5
postgres:
image: docker.io/library/postgres:15-alpine
container_name: gf-postgres
restart: unless-stopped
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_READ_SEARCH
- FOWNER
- SETGID
- SETUID
security_opt:
- no-new-privileges:true
env_file:
- ./.env
healthcheck:
test:
['CMD-SHELL', 'pg_isready -d "$${POSTGRES_DB}" -U $${POSTGRES_USER}']
interval: 10s
timeout: 5s
retries: 5
volumes:
- ./data/postgres:/var/lib/postgresql/data
redis:
image: docker.io/library/redis:alpine
container_name: gf-redis
restart: unless-stopped
user: '0:0' # 改成id $user查到的uid、gid
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
env_file:
- ./.env
command:
- /bin/sh
- -c
- redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
healthcheck:
test:
['CMD-SHELL', 'redis-cli --pass "$${REDIS_PASSWORD}" ping | grep PONG']
interval: 10s
timeout: 5s
retries: 5
volumes:
- ./data/redis:/data
# 没使用NPM则删去以下4行
networks:
default:
external: true
name: NPM_group最后运行即可:
cd /docker/ghostfolio
docker-compose config
docker-compose up由于笔者使用了NPM(nginx proxy manager),还需要在NPM后台设置一下映射。如果没用NPM,就从 http://localhost:3333 访问即可,如果有防火墙的记得打开:
ufw allow 3333 comment "ghostfolio"3.ghostfolio的基本使用与配置
(1)注册admin账号
登录网页后创建一个账号,并保存好令牌。第一个账号是admin管理员账号,然后可以在后台关闭注册( 管理-概述-用户注册 ,关闭)。
(2)添加货币、平台与资产
参考 官方网页 。
在 管理-市场数据 中,点击右下角的加号,添加货币CNY,添加自定义的资产。但是笔者遇到一些网络问题,yahoo不对大陆提供服务。
在 管理-设置 中,可以添加平台。
(3)数据导入
参考了 github-ghostfolio 。
ghostfolio可以导入交易数据,格式为:
{
"activities": [
{
"currency": "USD",
"dataSource": "YAHOO",
"date": "2021-09-15T00:00:00.000Z",
"fee": 19,
"quantity": 5,
"symbol": "MSFT",
"type": "BUY",
"unitPrice": 298.58
}
]
}每一项的解释为:
首先使用如下python代码将excel表格记录的所有交易转换为可以导入的形式(以下代码使用AI完成):
import pandas as pd
import json
from datetime import datetime
# ==================== 配置区 ====================
INPUT_FILE = "交易记录.xlsx" # 你的WPS表格文件
OUTPUT_FILE = "ghostfolio_import.json" # 输出的JSON文件
CURRENCY = "CNY" # 货币类型
DATA_SOURCE = "MANUAL" # 数据源,可选 YAHOO, COINGECKO 等
# =================================================
def parse_date(date_str):
"""将日期字符串转换为ISO-8601格式"""
# 处理 pandas 可能读取为 Timestamp 的情况
if isinstance(date_str, pd.Timestamp):
dt = date_str.to_pydatetime()
else:
# 尝试解析常见格式
for fmt in ("%Y-%m-%d %H:%M:%S", "%Y/%m/%d", "%Y-%m-%d"):
try:
dt = datetime.strptime(str(date_str).strip(), fmt)
break
except ValueError:
continue
else:
raise ValueError(f"无法解析日期: {date_str}")
return dt.strftime("%Y-%m-%dT00:00:00.000Z")
def extract_symbol(code_name):
"""从'代码/名称'中提取代码"""
if pd.isna(code_name):
return None
# 按 '/' 分割,取第一部分
parts = str(code_name).split("/")
return parts[0].strip()
def map_activity_type(op):
"""将操作类型映射为 Ghostfolio 的类型"""
op = str(op).strip()
if op == "买入":
return "BUY"
elif op == "卖出":
return "SELL"
elif op == "分红":
return "DIVIDEND"
else:
# 对于其他类型(如分红再投、期货转入等),暂时跳过
return None
def convert():
# 读取 Excel 文件,指定表头行
# 根据你提供的表格,第1行是表头(A1: 日期, B1: 品类, ...)
df = pd.read_excel(INPUT_FILE, header=0)
# 清理列名(去除首尾空格)
df.columns = df.columns.str.strip()
# 重置索引,避免 idx 为 Timestamp 等类型
df = df.reset_index(drop=True)
activities = []
skipped = 0
for i, (idx, row) in enumerate(df.iterrows(), start=2):
try:
# ---- 1. 日期 ----
date_val = row.get("日期")
if pd.isna(date_val):
skipped += 1
continue
date_iso = parse_date(date_val)
# ---- 2. 操作类型 ----
op = row.get("操作")
activity_type = map_activity_type(op)
if activity_type is None:
# 跳过不支持的类型(如分红再投、期货转入等)
skipped += 1
continue
# ---- 3. 代码 ----
symbol = extract_symbol(row.get("代码/名称"))
if not symbol:
skipped += 1
continue
# ---- 4. 单价 ----
unit_price = row.get("单价", 0)
if pd.isna(unit_price):
unit_price = 0.0
else:
unit_price = float(unit_price)
# ---- 5. 数量 ----
quantity = row.get("份额/股数", 0)
if pd.isna(quantity):
quantity = 0.0
else:
quantity = float(quantity)
# ---- 6. 手续费 ----
fee = row.get("手续费", 0)
if pd.isna(fee):
fee = 0.0
else:
fee = float(fee)
# ---- 7. 备注(可选) ----
comment = row.get("备注", "")
if pd.isna(comment):
comment = ""
else:
comment = str(comment).strip()
# ---- 构建 activity 对象 ----
activity = {
"currency": CURRENCY,
"dataSource": DATA_SOURCE,
"date": date_iso,
"fee": fee,
"quantity": quantity,
"symbol": symbol,
"type": activity_type,
"unitPrice": unit_price,
}
# 如果有备注,加入 comment 字段
if comment:
activity["comment"] = comment
activities.append(activity)
except Exception as e:
print(f"⚠️ 第 {i} 行处理失败: {e}")
skipped += 1
# 构建最终输出
output = {"activities": activities}
with open(OUTPUT_FILE, "w", encoding="utf-8") as f:
json.dump(output, f, ensure_ascii=False, indent=2)
print(f"✅ 转换完成!共生成 {len(activities)} 条活动记录。")
print(f"⏭️ 跳过 {skipped} 条(不支持的类型或数据异常)。")
print(f"📄 输出文件: {OUTPUT_FILE}")
if __name__ == "__main__":
convert()然后导入即可,但是要注意ghostfolio一次只能导入25条记录,还需要分割一下,这个就留给读者自行完成。
4.总结
缺点主要有:
(1)为美股设计,墙+没有股票代码,大陆无法自动获取数据,手动自定义资产太累,除非一开始就用这个。
(2)一次只能导入25条交易记录,整笑了。
(3)一笔交易要填数量、单价、手续费,对于股票来说可以,但是基金的计算是 (总价-手续费)/净值 ,它的计算方法是 总价=数量*单价+手续费 ,导致基金买卖有误差。
(4)后续不更新,以及数据泄露的风险。应该是21年推出的。
总的来说,还是excel表格稳定,不担心跑路。