Beancount(一款纯文本复式记账工具)和 Fava(它的 Web 界面)具有极强的可扩展性和可脚本化能力。它们的设计让你可以通过编写 Python 脚本来自动化财务任务、生成自定义报告、设置提醒。用一位用户的话来说:“我真的很喜欢把数据保存在这么方便的格式里,而且我喜欢可以随心所欲地自动化一切。没有什么 API 比得上你磁盘上的一个文件,它太容易集成了。” 本指南将带你走一遍创建可脚本化工作流的全过程——从适合初学者的自动化,到进阶的 Fava 插件。
探索一个实时示例账本:
从 bea 命令行开始
在写任何 Python 之前,先看看 bea 是否已经能完成这项任务。它会校验账本、运行 BQL 查询、生成四大财务报表、导入银行导出文件,而全局 --json 会把上述每一项都变成一个可解析的信封,供你的 shell 管道传给 jq。它的退出码是定时任务进行分支判断的契约,因此 cron 或 CI 完全不需要加载脚本。关于目标解析、信封与退出码分支,参见用 bea 自动化记账,当你需要 CLI 未暴露的自定义计算时再回到这里。
入门:将 Beancount 作为 Python 脚本运行
对于下面这些自定义 Python 脚本,先安装脚本库(pip install beancount beanquery beangulp)。bea 命令工作流改用托管引擎,请按 CLI 快速入门 安装。由于 Beancount 是用 Python 写的,你可以在自己的脚本里把它当作库来用。下面的脚本是用 Beancount 3.2.3、beanquery 0.2.0 和 beangulp 0.2.0 执行的。总体思路是:
-
加载你的 Beancount 账本:用 Beancount 的加载器把
.beancount文件解析成 Python 对象。例如:from beancount import loader entries, errors, options = loader.load_file("myledger.beancount") if errors: for error in errors: print(error) raise SystemExit(1)加载器会把 entries 和 errors 一起返回。不平衡或无效的文件依然会返回 entries,所以要先检查
errors,在信任数据之前就停下。现在你的所有账户、交易和余额都可以在代码中访问了。 -
利用 Beancount 查询语言(BQL):不必手动遍历,你可以对数据运行类 SQL 查询。查询位于独立的
beanquery包中。Beancount 3.2.3 里没有beancount.query模块。例如,要按月统计总支出,把加载好的 entries 传给查询并直接执行:import beanquery conn = beanquery.connect("beancount:", entries=entries, errors=errors, options=options) cur = conn.execute( "SELECT year, month, sum(position) WHERE account ~ 'Expenses' GROUP BY year, month" ) for row in cur.fetchall(): print(row)这里用 beanquery 做数据聚合。它和
bea query背后是同一个引擎,只不过这里你是在脚本里调用它,从而避免在循环里不断 shell 出去调用外部命令。 -
搭建项目结构:把你的脚本和账本放在一起组织。一种常见的布局是为 importers(获取/解析外部数据)、reports 或 queries(分析脚本)、documents(存放下载的对账单)分别建目录。例如,有位用户这样组织:
importers/—— 自定义 Python 导入脚本(带测试),queries/—— 生成报告的脚本(可通过python3 queries/...运行),documents/—— 按账户整理下载的银行 CSV/PDF。
有了这套结构,你可以手动运行脚本(比如 python3 queries/cash_flow.py),也可以用 cron 或任务调度器定时运行,实现工作流自动化。
自动化对账任务
对账就是确保你的账本与外部记录(银行对账单、信用卡报告等)一致。Beancount 的纯文本账本和 Python API 让自动化这其中大部分流程成为可能。
导入和匹配交易(入门)
对于初学者,推荐的做法是使用来自独立 beangulp 包的 importers。Beancount 3 移除了 v2 的 ingest 模块及其 extract 命令。你写一个小小的 Python 类,继承 beangulp.Importer,用来解析某种格式(CSV、OFX、PDF 等)并生成交易。把它注册到一个简短的 ingest 脚本里,然后在托管引擎中通过 bea ingest 运行:
- 为你的银行 CSV 格式写一个 importer(一个 Python 类,包含
identify()、account()和extract()方法)。 - 添加一个 ingest 脚本,把 importer 注册进去。
bea ingest会运行该脚本的identify、extract和archive命令。例如,有个工作流会对~/Downloads里的所有文件运行extract,把交易输出到一个临时文件。 - 手工审阅临时文件里的交易并复制到你的主账本中,然后运行
bea check确保余额对得上。
一个最小示例:statement.csv 含 date,description,amount 列,由下面这个 importer(checking_importer.py)解析:
import csv
import datetime
from beancount.core import data
from beancount.core.amount import Amount
from beancount.core.number import D
import beangulp
class CheckingImporter(beangulp.Importer):
def identify(self, filepath: str) -> bool:
return filepath.endswith("statement.csv")
def account(self, filepath: str) -> str:
return "Assets:Bank:Checking"
def extract(self, filepath: str, existing):
entries = []
with open(filepath, newline="") as f:
for row in csv.DictReader(f):
date = datetime.date.fromisoformat(row["date"])
amount = Amount(D(row["amount"]), "USD")
meta = data.new_metadata(filepath, 0)
entries.append(
data.Transaction(
meta, date, "*", None, row["description"],
data.EMPTY_SET, data.EMPTY_SET, [
data.Posting("Expenses:Food:Groceries", amount,
None, None, None, None),
data.Posting("Assets:Bank:Checking",
Amount(-amount.number, "USD"),
None, None, None, None),
]))
return entriesingest 脚本(ingest.py)把它串起来:
from checking_importer import CheckingImporter
from beangulp import Ingest
ingest = Ingest([CheckingImporter()])
if __name__ == "__main__":
ingest()对下载的文件运行它。本地 CSV 不需要任何凭据。先安装系统的 libmagic 库。这条一次性启用命令会把 Beangulp 下载进托管引擎:
bea engine enable beangulp
bea ingest identify --config ingest.py statement.csv
bea ingest extract --config ingest.py statement.csv -o new.beancountidentify 对该文件报告 checking_importer.CheckingImporter。extract 会以 Beancount 格式写出交易:
2024-01-08 * "Grocery Store"
Expenses:Food:Groceries 120.00 USD
Assets:Bank:Checking -120.00 USD审阅 new.beancount,把条目复制进你的主账本,然后运行 bea check。
要转换单份对账单,不一定非得写 importer。把文件粘进 CSV 转 Beancount 转换器,或者对 .ofx、.qfx 和 .qif 下载用 OFX 与 QIF 转 Beancount。这两个工具完全在浏览器里运行,所以对账单永远不会离开你的机器。
虽然这个过程仍然有一步人工审阅,但解析和格式化条目的大量苦活都自动化了。Importer 脚本还可以自动分配分类,甚至设置余额断言(对预期余额的声明),以便捕捉差异。例如,导入后你可能有一行 2025-04-30 balance Assets:Bank:Checking 1234.56 USD,它断言了期末余额。当你运行 bea check 时,Beancount 会_验证所有这些余额断言是否正确_,如果交易缺失或重复就会标记出错误。这是一个最佳实践:为每个对账单周期自动生成余额断言,让计算机替你发现未对平的差异。
自定义对账脚本(中级)
想要更多控制权,你可以写一个自定义 Python 脚本来比对银行的交易列表(CSV 或通过 API)与你的账本条目:
- 读取外部数据:用 Python 的
csv模块(或 Pandas)解析银行 CSV 文件。把数据规范化为一个交易列表,比如每笔都包含日期、金额和描述。 - 加载账本交易:像前面那样用
loader.load_file拿到所有账本条目。把这个列表过滤到相关账户(比如你的活期账户),也许再按对账单的日期范围过滤。 - 比对并找出不匹配:
- 对每笔外部交易,检查账本里是否已存在完全相同的条目(按日期和金额匹配,可能还要看描述)。如果没找到,就标记为“新”的,也许把它输出成一条 Beancount 格式的交易供你审阅。
- 反过来,找出该账户里那些没有出现在外部数据源中的账本条目——它们可能是录入错误,或是尚未在银行清算的交易。
- 输出结果:打印一份报告,或者用缺失的交易创建一个新的
.beancount片段。
举个例子,一个叫 reconcile.py 的社区脚本正是干这个的:给定一个 Beancount 文件和一个输入的 CSV,它会打印一份应被导入的新交易列表,以及任何不在输入中的现有账本过账(可能是分类错误的迹象)。有了这样的脚本,每月对账就简单到只需运行它,然后把建议的交易追加到账本里。一位 Beancount 用户说,他们“每个月都会对所有账户做一次对账流程”,并用越来越多的 Python 代码来消除导入和对账数据中的大量手工工作。
小贴士: 对账时,利用 Beancount 的工具来保证准确性:
- 如前所述,使用余额断言,对账户余额做自动化检查。
- 如有需要,使用
pad指令,它可以为微小的舍入差异自动插入平衡条目(谨慎使用)。 - 为你的 importer 或对账逻辑写单元测试(Beancount 提供了测试辅助工具)。例如,有个工作流是取一份样本 CSV,写出带预期交易的失败测试,然后实现 importer 直到所有测试通过。这能确保你的导入脚本在各种情况下都能正确工作。
生成自定义报告和摘要
虽然 Fava 提供了许多标准报表(利润表、资产负债表等),你仍可以用脚本创建自定义报告。它们可以从简单的控制台输出,到格式丰富的文件或图表。
查询数据以生成报告(初级)
在最基础的层面,你可以用 Beancount 查询语言(BQL)拿到汇总数据,然后打印或保存。例如:
-
现金流摘要: 用查询计算净现金流。“现金流”可以定义为某些账户余额在某段时间内的变化。用 BQL,你可以这样写:
SELECT year, month, sum(position) WHERE account ~ 'Income' OR account ~ 'Expenses' GROUP BY year, month这会按月份把所有收入与支出过账净额算出来。用
~加正则表达式来过滤:在 beanquery 0.2.0 里LIKE是语法错误。过账携带的是position,不是amount。每一行持有的是一个 Inventory,所以每种货币都单独列出,而不是被换算到一起。收入是负值、支出是正值。你可以通过bea query运行它,也可以像前面那样用 beanquery 的 Python API,然后再格式化结果。 -
分类支出报告: 按分类查询总支出:
SELECT account, sum(position) WHERE account ~ 'Expenses' GROUP BY account ORDER BY sum(position) ASC这会生成一张按分类的支出表。每个合计都是一个以其原始货币计价的 Inventory。不要给聚合结果套上
round():不存在round(inventory, int)这样的函数,所以round(sum(position), 2)无法编译。你可以在一个脚本里跑多个查询,并把结果输出为文本、CSV 甚至 JSON 供进一步处理。
有位用户觉得用 Fava 或脚本分析财务数据是件“轻而易举”的事,他说自己用一个 Python 脚本通过查询语言从 Beancount 中取出数据,然后放进 Pandas DataFrame 里准备一份自定义报告。比如,你可以用查询取出月度合计,然后用 Pandas/Matplotlib 画一张随时间变化的现金流图。BQL 与数据科学库的组合,让你能构建出超出 Fava 默认能力的报告。
高级报告(图表、性能等)
对于更进阶的需求,你的脚本可以计算诸如投资业绩之类的指标,或创建可视化输出:
-
投资业绩(IRR/XIRR):由于你的账本包含了所有现金流(买入、卖出、分红),你可以计算组合收益率。例如,你可以写一个脚本,筛选出投资账户的交易,然后计算内部收益率。有现成的库(或公式)可以在给定现金流数据的情况下计算 IRR。一些社区开发的 Fava 扩展(如 PortfolioSummary 或 fava_investor)正是这么做的,为投资组合计算 IRR 及其他指标。作为脚本,你可以对一系列投入/取出加上期末价值,套用一个 IRR 函数(来自 NumPy 或你自己写的)。
-
多周期或自定义指标:想要一份每月储蓄率(储蓄与收入之比)的报告?一个 Python 脚本可以加载账本,汇总所有 Income 账户和所有 Expense 账户,然后计算 savings = income - expenses 及其百分比。这可以输出一张漂亮的表,甚至生成一份 HTML/Markdown 报告存档。
-
可视化:你可以在 Fava 之外生成图表。例如,在脚本里用
matplotlib或altair,基于账本数据画一张净资产随时间变化图。因为账本有全部历史余额(或者你可以遍历条目累加出来),你可以生成时间序列图。把这些图表保存为图片或交互式 HTML。(如果你更喜欢应用内可视化,参见下面的 Fava 扩展一节,了解如何在 Fava _内部_添加图表。)
输出选项: 决定如何交付报告:
- 对于一次性分析,打印到屏幕或保存为 CSV/Excel 文件可能就够了。
- 对于仪表盘,可以考虑把数据生成一个 HTML 文件(可以使用 Jinja2 之类的模板库,甚至直接写 Markdown),在浏览器中打开。
- 你也可以接入 Jupyter Notebook 获得交互式报告环境,不过那更偏向探索而非自动化。
从你的账本触发警报
可脚本化工作流的另一个强大用途,是根据财务数据中的条件设置提醒。由于你的账本会被定期更新(而且可以包含未来日期的事项,如即将到期的账单或预算),你可以用脚本扫描它,并在发生重要事件时收到通知。
低账户余额警告
为了避免透支或维持最低余额,你可能希望在任何账户(比如活期或储蓄)跌破阈值时收到提醒。可以这样实现:
-
确定当前余额:通过 loader 加载
entries后,计算相关账户的最新余额。你可以通过聚合过账或使用查询来做。例如,用 BQL 查询某个特定账户的余额:SELECT sum(position) WHERE account = 'Assets:Bank:Checking'这会返回该账户的当前余额(其所有过账之和)。或者,用 Beancount 的内部函数构建资产负债表。例如:
from beancount.core import realization tree = realization.realize(entries) acct = realization.get_or_create(tree, "Assets:Bank:Checking") balance = acct.balance # an Inventory of commodities只传 entries:第二个参数是
min_accounts,不是 options 映射。然后取出数值(比如balance.get_currency_units('USD')返回以 USD 计的 Decimal 金额)。和查询聚合一样,余额会把每种货币单独保留。不过对大多数情况来说,用查询更简单。 -
检查阈值:把余额与你预设的限额比较。如果低于它,就触发提醒。
-
触发通知:这可以简单到向控制台打印一条警告,但要真正做提醒,你可能会发邮件或推送通知。你可以接入邮件(通过
smtplib)或 IFTTT、Slack 的 webhook API 之类的服务来推送提醒。例如:if balance < 1000: send_email("Low balance alert", f"Account XYZ balance is {balance}")(用你自己的邮件服务器信息实现
send_email。)
通过每天运行这个脚本(用 cron 任务或 Windows 任务计划程序),你就能获得主动预警。因为它用的是账本,所以能把包括你刚添加的在内的_所有_交易都考虑进去。
即将到来的付款截止日期
如果你用 Beancount 追踪账单或截止日期,你可以标记未来付款,并让脚本提醒你。在 Beancount 中表示未来义务的两种方式:
-
Events:Beancount 支持用
event指令记任意带日期的备注。例如:2025-05-10 event "BillDue" "Mortgage payment due"这不会影响余额,但会记录一个带标签的日期。脚本可以扫描
entries中Event.type == "BillDue"(或你选择的任何自定义类型)的Event条目,并检查日期是否在比如从今天起 7 天之内。如果是,就触发提醒(邮件、通知,甚至弹窗)。 -
未来交易:有些人会为预定付款等事项录入未来日期的交易(倒填日期)。这些在日期到来之前不会出现在余额里(除非你按未来日期出报表)。脚本可以找出日期在不久未来的交易并列出它们。
利用这些,你可以做一个“备忘”脚本,运行时就输出一份即将到期的任务或账单列表。如果你想让它在 Google Calendar 或某个任务管理器里自动创建提醒,就接入相应的 API。
异常检测
除了已知的阈值或日期,你还可以为异常模式编写自定义提醒。比如,一项通常按月发生的支出没有出现(也许你忘了付账单),或者某个分类本月的支出异常高,你的脚本都可以把它标记出来。这通常需要查询近期数据并与历史比较(这可能是个进阶话题——也许会用到统计或机器学习)。
在实践中,很多用户依靠对账来捕捉异常(意外交易)。如果你会收到银行通知(比如每笔交易的邮件),你可以用脚本解析它们并自动加入 Beancount,或者至少核实它们已被记录。有一位爱好者甚至把银行配置成发送交易提醒邮件,打算自动解析并追加到账本。这类事件驱动的提醒可以确保没有交易会漏记。
使用自定义插件和视图扩展 Fava
Fava 本身就已经通过它的扩展系统实现了可脚本化。如果你想让自动化或报告直接集成到 Web 界面里,你可以用 Python 写一个 Fava 扩展(也叫插件)。
Fava 扩展如何工作: 一个扩展就是一个 Python 模块,定义了一个继承 fava.ext.FavaExtensionBase 的类。你通过一个自定义选项把它注册到 Beancount 文件里。例如,如果你有个文件 myextension.py,里面有个类 MyAlerts(FavaExtensionBase),你就可以在账本里加入以下内容来启用它:
1970-01-01 custom "fava-extension" "myextension"Fava 加载时,会导入那个模块并初始化你的 MyAlerts 类。
扩展可以做几件事:
- 钩子(Hooks):它们可以挂进 Fava 生命周期中的事件。例如,
after_load_file()会在账本加载后被调用。你可以用它来运行检查或预先计算数据。如果你想在 Fava _内部_实现低余额检查,after_load_file可以遍历账户余额,也许把警告存起来(不过要把它们呈现在 UI 上可能还需多做点工作,比如抛出 FavaAPIError 或用 JavaScript 显示通知)。 - 自定义报表/页面:如果你的扩展类设置了
report_title属性,Fava 会在侧边栏为它新增一个页面。然后你为该页面的内容提供一个模板(HTML/Jinja2)。这就是你创建全新视图的方式,比如 Fava 默认没有的仪表盘或摘要。扩展可以收集它需要的任何数据(你可以访问self.ledger,它有所有条目、余额等),然后渲染模板。
例如,Fava 内置的 portfolio_list 扩展会新增一个列出你投资组合持仓的页面。社区扩展走得更远:
- 仪表盘:fava-dashboards 插件允许定义自定义图表和面板(使用 Apache ECharts 之类的库)。它读取一份要运行的查询的 YAML 配置,通过 Beancount 执行它们,并在 Fava 中生成一个动态仪表盘页面。本质上,它把 Beancount 数据和 JavaScript 图表库绑在一起,生成交互式可视化。
- 投资组合分析:PortfolioSummary 扩展(用户贡献)会计算投资摘要(分组账户、计算 IRR 等),并在 Fava 的 UI 中显示出来。
- 交易审查:另一个扩展 fava-review 帮助按时间审查交易(比如确保你没漏掉任何收据)。
要自己创建一个简单扩展,先从继承 FavaExtensionBase 开始。例如,一个新增页面的最小扩展可能长这样:
from fava.ext import FavaExtensionBase
class HelloReport(FavaExtensionBase):
report_title = "Hello World"
def __init__(self, ledger, config):
super().__init__(ledger, config)
# any initialization, perhaps parse config if provided
def after_load_file(self):
# (optional) run after ledger is loaded
print("Ledger loaded with", len(self.ledger.entries), "entries")如果你把它放进 hello.py,并在账本里加上 custom "fava-extension" "hello",Fava 就会显示一个新的“Hello World”页面(你还需要在 templates 子文件夹里有一个模板文件 HelloReport.html 来定义页面内容,除非该扩展只用钩子)。模板可以使用你附加到扩展类上的数据。Fava 使用 Jinja2 模板,所以你可以在那个模板里把你的数据渲染成 HTML 表格或图表。
注意: Fava 的扩展系统很强大,但被认为“不稳定”(随时可能变化)。如果你要做自定义页面,它需要你对 Web 开发(HTML/JS)有一定了解。如果你的目标只是运行脚本或分析,把它们保持为外部脚本可能更容易。当你想要为你的工作流打造量身定制的应用内体验时,再用 Fava 扩展。
集成第三方API和数据
可脚本化工作流的优势之一,就是能引入外部数据。以下是一些常见的集成:
对于托管估值价格,实时价格 提供托管式的 include,无需定时抓取价格的脚本。在选择器里挑好支持的资产对和计价货币即可。下面这些本地文件式工作流对于上游 Beancount、Fava 和可复现报告依然有用。托管刷新不会在你的账本里产生 Git 提交。
-
汇率与商品:上游 Beancount 本身不抓价格,但它提供
price指令让你提供汇率。你可以自动化抓取这些价格。例如,脚本可以查询某个 API(Yahoo Finance、Alpha Vantage 等)获取最新汇率或股价,并追加一条 price 条目到你的账本:2025-04-30 price BTC 30000 USD 2025-04-30 price EUR 1.10 USD有
bea price这样的工具,由托管引擎中的 Beanprice 支持,可以抓取每日报价并以 Beancount 格式输出。你可以用bea engine enable beanprice一次性启用它,然后安排bea price main.beancount每晚运行,更新一个prices.beancountinclude 文件。或者用 Python:比如用requests库调用 API。Beancount 的文档建议,对于公开交易的资产,你可以“调用一些代码来下载价格并为你写出相应指令。” 换句话说,让脚本去查询并插入price行,而不是你手动去做。 -
股票投资组合数据:和汇率类似,你可以接入 API 获取详细的股票数据或分红。比如 Yahoo Finance API(或
yfinance之类的社区库)可以取某只股票的历史数据。脚本可以用你持有的每只股票的月度价格历史更新账本,从而实现准确的历史市值报告。一些自定义扩展(如 fava_investor)甚至会在展示时即时抓取价格数据,但最简单的是定期把价格导入账本。 -
银行 API(开放银行/Plaid):除了下载 CSV,你还可以用 API 自动获取交易。像 Plaid 这样的服务会聚合银行账户,并允许程序化访问交易。在进阶设置中,你可以用一个 Python 脚本通过 Plaid 的 API 每天拉取新交易并保存到文件(或直接导入账本)。有位高阶用户搭建了一套系统,让 Plaid 喂进他的导入流水线,使他的账本几乎全自动。他指出,“没有什么能阻止你注册 Plaid API 并在本地做同样的事”——也就是说,你可以写一个本地脚本获取银行数据,然后用你的 Beancount importer 逻辑把它解析成账本条目。有些地区有银行提供的开放银行 API,也可以类似地使用。
-
其他 API:你可以集成本预算工具(导出计划预算,与 Beancount 中的实际数比较),或者用 OCR API 读取收据并自动匹配到交易。因为你的脚本能完全访问 Python 生态,你可以集成从邮件服务(发送提醒)到 Google Sheets(比如把每月财务指标更新到表格)再到消息应用(通过 Telegram 机器人给自己发一份摘要报告)的一切。
使用第三方 API 时,记得保管好你的凭据(API 密钥用环境变量或配置文件),并在脚本中优雅地处理错误(网络问题、API 宕机)。通常明智的做法是缓存数据(例如,把抓取到的汇率存起来,免得反复请求同一个历史汇率)。
模块化、可维护脚本的最佳实践
随着你搭建可脚本化工作流,让代码保持有条理且健壮:
-
模块化:把不同关注点拆进不同的脚本或模块。例如,把“数据导入/对账”与“报告生成”与“提醒”分成不同脚本。你甚至可以给你的账本建一个小 Python 包,模块如
ledger_import.py、ledger_reports.py等。这让每一部分都更易理解和测试。 -
配置:避免硬编码值。把账户名、阈值、API 密钥、日期范围等用配置文件或脚本顶部变量来定义。这让调整无需深入改代码。例如,在顶部定义
LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000},你的提醒脚本就可以遍历这个 dict。 -
测试:把你的财务自动化当成关键任务代码来对待——因为它确实是!为复杂逻辑写测试。Beancount 提供了一些测试辅助工具(内部用于 importer 测试),你可以利用它们来模拟账本输入。即便不用花哨的框架,你也可以拿一份假 CSV 和预期的输出交易,断言你的导入脚本生成正确的条目。如果你用
pytest,可以轻松集成这些测试(就像 Alex Watt 用一个包裹 pytest 的just test命令所做的那样)。 -
版本控制:把你的账本和脚本纳入版本控制(git)。这不仅给你备份和历史,还鼓励你以受控的方式做改动。你可以给你的“财务脚本”打发布标签,或在调试问题时审阅差异。有些用户甚至用 Git 追踪他们的财务记录,以观察随时间的变化。只是要注意在仓库里忽略敏感数据(如原始对账单文件或 API 密钥)。
-
文档:为未来的自己记录你的自定义工作流。仓库里一份 README,说明如何搭建环境、如何运行每个脚本、每个脚本做什么,在几个月之后会价值连城。也给代码加注释,特别是任何不显然的会计逻辑或 API 交互。
-
Fava 插件维护:如果你写 Fava 扩展,保持简单。Fava 可能会变,所以功能更有针对性的小扩展更容易更新。避免重复太多逻辑——尽可能用 Beancount 的查询引擎或已有的辅助函数,而不是硬编码可能对账本变化敏感的计算。
-
安全:由于你的脚本可能处理敏感数据并连接外部服务,要小心对待。不要暴露 API 密钥,并考虑在安全的机器上运行你的自动化。如果你用托管方案或云(比如用 GitHub Actions 或某台服务器跑 Fava),确保你的账本数据在静态时加密,并确保你对隐私影响感到放心。
遵循这些实践,你就能确保即便你的财务状况(以及工具本身)在演变,你的工作流依然可靠。你要的是能年复一年复用、只需极小调整的脚本。
结论
Beancount 和 Fava 为技术娴熟的用户提供了一个强大、灵活的平台,来完全自定义个人财务追踪。通过编写 Python 脚本,你可以自动化对账等繁琐任务,产出为你量身定制的丰富报告,并通过及时提醒把自己的财务尽在掌握。我们涵盖了一系列从基础到进阶的示例——从简单查询和 CSV 导入,到完整的 Fava 插件和外部 API 集成。在实现这些时,从简单开始、逐步构建。哪怕只是几个小的自动化脚本,也能节省数小时的工作并大幅提升准确性。记住,因为一切都是纯文本和 Python,你拥有完全的控制权——你的财务系统随你一起成长,随你的具体需求弯曲。祝脚本愉快!
来源: 上述技巧取自 Beancount 文档和社区经验。想进一步阅读,参见 Beancount 官方文档、社区指南和博客,以及 Awesome Beancount 仓库,里面有指向有用插件和工具的链接。