使用 Beancount 和 Fava 的可编写脚本的工作流
Beancount(一种纯文本复式记账工具)和 Fava(其 Web 界面)具有高度的可扩展性和可编写脚本性。它们的设计允许你通过编写 Python 脚本来自动化财务任务、生成自定义报告和设置警报。正如一位用户所说,“我真的很喜欢以如此方便的格式存储我的数据,而且我喜欢我可以随心所欲地自动化事情。没有像磁盘上的文件那样的 API;它很容易集成。” 本指南将引导你创建可编写脚本的工作流——从初学者友好的自动化到高级 Fava 插件。
入门:将 Beancount 作为 Python 脚本运行
在深入了解具体任务之前,请确保已安装 Beancount(例如,通过 pip install beancount)。由于 Beancount 是用 Python 编写的,因此你可以在自己的脚本中将其用作库。一般方法 是:
-
加载你的 Beancount 账本:使用 Beancount 的加载器将
.beancount文件解析为 Python 对象。例如:from beancount import loader
entries, errors, options_map = loader.load_file("myledger.beancount")
if errors:
print("Errors:", errors)这为你提供了一个
entries列表(交易、余额等)和一个包含元数据的options_map。现在可以在代码中访问你的所有帐户、交易和余额。 -
利用 Beancount 查询语言 (BQL):你可以运行类似于 SQL 的查询来查询数据,而无需手动迭代。例如,要按月获取总支出,你可以使用查询 API:
from beancount.query import query
q = query.Query(entries, options_map)
result = q.query("SELECT month, sum(position) WHERE account ~ 'Expenses' GROUP BY month")
print(result)这使用 Beancount 的查询系统来聚合数据。(在底层,这类似于
bean-query命令所做的事情,但在这里你在脚本中使用它。) 事实上,Beancount 的作者指出,你可以加载文件并直接通过 Python API 调用run_query(),从而避免了在循环中调用外部命令的需要。 -
设置项目结构:将你的脚本与你的账本一起组织。常见的布局是为 importers(用于获取/解析外部数据)、reports 或 queries(用于分析脚本)和 documents(用于存储下载的报表)创建目录。例如,一位用户保留:
importers/– 自定义 Python 导入脚本(带有测试),queries/– 用于生成报告的脚本(可通过python3 queries/...运行),documents/– 按帐户组织的下载的银行 CSV/PDF。
通过此设置,你可以手动运行脚本(例如 python3 queries/cash_flow.py)或安排它们(通过 cron 或任务运行器)以自动化你的工作流程。
自动化对账任务
对账 意味着确保你的账本与外部记录(银行对账单、信用卡报告等)匹配。Beancount 的纯文本账本和 Python API 使自动化此过程的大部分成为可能。
导入和匹配交易(初学者)
对于初学者,建议的方法是使用 Beancount 的 导入器插件。你编写一个小型的 Python 类,遵循 Beancount 的导入器协议来解析给定的格式(CSV、OFX、PDF 等)并生成交易。然后使用 bean-extract 命令或脚本来应用这些导入器:
- 为你银行的 CSV 格式编写一个导入器(一个 Python 类,其中包含
identify()、extract()等方法)。Beancount 的文档提供了指南和示例。 - 在脚本或 Makefile(如
justfile示例)中使用bean-extract来解析新的报表。例如,一个工作流程在~/Downloads中的所有文件上运行bean-extract并将交易输出到临时文件。 - 手动审查并将交易从临时文件复制到你的主账本中,然后运行
bean-check以确保余额对账。
虽然此过程仍涉及审查步骤,但解析和格式化条目的大部分繁琐工作已自动化。导入器脚本还可以自动分配类别,甚至设置余额断言(预期余额的声明)以捕获差异。例如,导入 后,你可能会有类似 2025-04-30 balance Assets:Bank:Checking 1234.56 USD 的行,该行断言期末余额。当你运行 bean-check 时,Beancount 将 验证所有这些余额断言是否正确,如果缺少或重复交易,则会标记任何错误。这是一个最佳实践:为每个报表期自动生成余额断言,让计算机为你发现未对账的差异。
自定义对账脚本(中级)
为了获得更多控制,你可以编写一个自定义 Python 脚本来比较银行的交易列表(CSV 或通过 API)与你的账本条目:
- 读取外部数据:使用 Python 的
csv模块(或 Pandas)解析银行的 CSV 文件。将数据标准化为交易列表,例如,每个交易都具有日期、金额和描述。 - 加载账本交易:如前所示使用
loader.load_file获取所有账本条目。将此列表过滤到感兴趣的帐户(例如,你的支票帐户)以及报表的日期范围。 - 比较并查找不匹配项:
- 对于每个外部交易,检查账本中是否存在相同的条目(按日期和金额匹配,可能还有描述)。如果未找到,则将其标记为“new”并可能将其输出为 Beancount 格式的交易供你审核。
- 相反,识别该帐户中未出现在外部来源中的任何账本条目——这些可能是数据输入错误或尚未清算的交易。
- 输出结果:打印报告或创建一个新的
.beancount代码段,其中包含缺少的交易。
例如,一个名为 reconcile.py 的社区脚本正是这样做的:给定一个 Beancount 文件和一个输入 CSV,它会打印一个应该导入的新交易列表,以及输入中不存在的任何现有账本过账(可能表示错误分类)。使用这样的脚本,每月对账可以像运行它然后将建议的交易附加到你的账本一样简单。一位 Beancount 用户指出,他们 “每个月都对所有帐户执行对账过程” 并使用不断增长的 Python 代码集合来消除导入和对账数据中的大部分手动工作。
提示: 在对账期间,利用 Beancount 的工具来提高准确性:
- 如前所述,使用余额断言,对帐户余额进行自动检查。
- 如果需要,使用
pad指令,它可以自动插入平衡条目以解决较小的舍入差异(谨慎使用)。 - 为你的导入器或对账逻辑编写单元测试(Beancount 提供测试帮助程序)。例如,一个工作流程涉及获取一个示例 CSV,编写具有预期交易的失败测试,然后实现导入器直到所有测试通过。这可确保你的导入脚本适用于各种情况。
生成自定义报告和摘要
虽然 Fava 提供了许多标准报告(损益表、资产负债表等),但你可以使用脚本创建自定义报告。这些报告的范围可以从简单的控制台输出到格式丰富的格式文件或图表。