跳转到主要内容

如何用Python脚本自动化Beancount和Fava

Beancount 与 Fava 保持可脚本化:使用 Python 自动生成报告、查询余额,并对账本运行自定义工作流以及日常批处理任务脚本。

Beancount(一种纯文本复式记账工具)和 Fava(其 Web 界面)具有高度可扩展性和可脚本化性。它们的设计允许你通过编写 Python 脚本来自动化财务任务、生成自定义报告以及设置警报。用一位用户的话说,“我真的很喜欢我的数据能以如此方便的格式存在,我也喜欢可以随心所欲地自动化一切。没有什么 API 能比得上磁盘上的文件;它很容易集成。” 本指南将带你逐步创建可脚本化的工作流——从入门级自动化到高级 Fava 插件

探索一个实时示例账本:

在新标签页中打开 Example Ledger

从 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)

    加载器会同时返回条目错误。即使文件不平衡或无效,也会返回条目,因此请检查 errors 并在信任数据之前停止。你的所有账户交易和余额现在都可以在代码中访问了。

  • 利用 Beancount 查询语言 (BQL):无需手动迭代,你可以对数据运行类似 SQL 的查询。查询位于独立的 beanquery 包中。Beancount 3.2.3 中没有 beancount.query 模块。例如,要按月份获取总支出,可以连接加载的条目并直接执行查询:

    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/ – 自定义 Python 导入脚本(带有测试),
    • queries/ – 生成报告的脚本(可通过 python3 queries/... 运行),
    • documents/ – 按账户组织的下载银行 CSV/PDF 文件。

使用这种设置,你可以手动运行脚本(例如 python3 queries/cash_flow.py)或通过 cron 或任务运行器安排它们来自动化你的工作流。

自动化对账任务

对账意味着确保你的账本与外部记录(银行对账单、信用卡账单等)保持一致。Beancount 的纯文本账本和 Python API 使得自动化这一过程的大部分成为可能。

导入和匹配交易(入门)

对于初学者,推荐的方法是使用来自独立 beangulp 包的导入器。Beancount 3 移除了 v2 的 ingest 模块及其 extract 命令。你编写一个小型 Python 类,继承 beangulp.Importer 来解析给定格式(CSV、OFX、PDF 等)并生成交易。在一个简短的 ingest 脚本中注册它,然后通过受管引擎中的 bea ingest 运行它:

  • 为你的银行CSV格式编写一个导入器(一个包含identify()account()extract()方法的Python类)。
  • 添加一个ingest脚本,注册你的导入器。bea ingest运行脚本的identifyextractarchive命令。例如,一个工作流对~/Downloads中的所有文件运行extract,并将交易输出到一个临时文件。
  • 手动审查并从临时文件复制交易到你的主账本中,然后运行bea check以确保余额对账。

一个最小示例:一个包含date,description,amount列的statement.csv文件,由这个导入器(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 entries

ingest脚本(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.beancount

identify为该文件报告checking_importer.CheckingImporterextract以Beancount格式写入交易:

2024-01-08 * "Grocery Store"
  Expenses:Food:Groceries   120.00 USD
  Assets:Bank:Checking     -120.00 USD

审查new.beancount,将条目复制到你的主账本中,然后运行bea check

跳过导入器进行一次性转换

你不需要编写导入器来转换单个报表。将文件粘贴到CSV转Beancount转换器,或者对于.ofx.qfx.qif下载使用OFX与QIF转Beancount。两者完全在你的浏览器中运行,因此报表永远不会离开你的机器。

虽然这个过程仍然涉及审查步骤,但解析和格式化条目的大量繁琐工作已自动化。导入器脚本还可以自动分配类别,甚至设置余额断言(预期余额的声明)以捕捉差异。例如,导入后,你可能有一行如2025-04-30 balance Assets:Bank:Checking 1234.56 USD,它断言了期末余额。当你运行bea check时,Beancount将_验证所有这些余额断言是否正确_,并在交易缺失或重复时标记错误。这是一种最佳实践:为每个报表期自动生成余额断言,让计算机为你发现未对平的差异。

自定义对账脚本(中级)

为了获得更多控制,你可以编写一个自定义Python脚本来比较银行的交易列表(CSV或通过API)与你的账本条目:

  1. 读取外部数据:使用Python的csv模块(或Pandas)解析银行的CSV文件。将数据规范化为交易列表,例如每个交易包含日期、金额和描述。
  2. 加载账本交易:如前所述使用loader.load_file获取所有账本条目。将此列表过滤到感兴趣的账户(例如你的支票账户),可能还有报表的日期范围。
  3. 比较并找出不匹配
  • 对于每个外部交易,检查账本中是否存在完全相同的条目(按日期、金额,可能还有描述匹配)。如果未找到,将其标记为“新”交易,并可能将其输出为Beancount格式的交易供你审查。
  • 反之,识别该账户中不在外部源中出现的账本条目——这些可能是数据输入错误或尚未在银行清算的交易。
  1. 输出结果:打印报告或创建一个包含缺失交易的新.beancount片段。

例如,一个名为**reconcile.py的社区脚本正是这样做的:给定一个Beancount文件和一个输入CSV,它打印出应导入的新交易列表,以及输入中不存在的现有账本过账(这可能是分类错误的迹象)。有了这样的脚本,月度对账可以简单到运行它,然后将建议的交易附加到账本中。一位Beancount用户指出,他们_“每月对所有账户执行对账过程”_,并使用不断增长的Python代码集合来消除导入和对账中的大量手动工作**。

**提示:**在对账过程中,利用Beancount的工具来提高准确性:

  • 使用余额断言,如所述,对账户余额进行自动化检查。
  • 如果需要,可以使用 pad 指令,该指令可以自动插入平衡条目以处理轻微的舍入差异(请谨慎使用)。
  • 为你的导入器或对账逻辑编写单元测试(Beancount 提供了测试助手)。例如,一个工作流程涉及获取示例 CSV,编写失败的测试并预期交易,然后实现导入器,直到所有测试通过。这可以确保你的导入脚本在各种情况下都能正确工作。

生成自定义报告和摘要

虽然 Fava 提供了许多标准报告(如损益表、资产负债表等),但你可以使用脚本创建自定义报告。这些报告可以是简单的控制台输出,也可以是丰富的格式化文件或图表。

查询数据以生成报告(初级)

在基础层面,你可以使用 Beancount 查询语言(BQL)来获取汇总数据并打印或保存它。例如:

  • 现金流摘要: 使用查询来计算净现金流。“现金流”可以定义为在一段时间内某些账户的余额变化。使用 BQL,你可以这样做:

    SELECT year, month, sum(position)
    WHERE account ~ 'Income' OR account ~ 'Expenses'
    GROUP BY year, month

    这将按月份对所有的收入和支出记录进行净额计算。使用 ~ 和正则表达式进行过滤:LIKE 在 beanquery 0.2.0 中是语法错误。记录携带的是 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 扩展(如 PortfolioSummaryfava_investor)正是这样做的,计算投资组合的 IRR 和其他指标。作为脚本,你可以使用 IRR 函数(来自 NumPy 或你自己编写的)对一系列贡献/提款加上期末值进行计算。

  • 多周期或自定义指标: 想要一份每月储蓄率(储蓄与收入的比值)的报告?一个 Python 脚本可以加载账本,汇总所有收入账户和支出账户,然后计算储蓄 = 收入 - 支出以及百分比。这可以输出一个漂亮的表格,甚至生成一份 HTML/Markdown 报告供你记录。

  • 可视化: 你可以在 Fava 之外生成图表。例如,在脚本中使用 matplotlibaltair 创建净值随时间变化的图表,使用账本数据。由于账本包含所有历史余额(或者你可以通过遍历条目累积它们),你可以生成时间序列图。将这些图表保存为图片或交互式 HTML。(如果你更喜欢应用内可视化,请参阅下面的 Fava 扩展部分,了解如何在 Fava 中添加图表。)

输出选项: 决定如何交付报告:

  • 对于一次性分析,打印到屏幕或保存为 CSV/Excel 文件就足够了。
  • 对于仪表盘,考虑生成一个带有数据的 HTML 文件(可能使用像 Jinja2 这样的模板库,甚至只写 Markdown)以便在浏览器中打开。
  • 你也可以集成 Jupyter Notebook 以获得交互式报告环境,尽管这更适用于探索而非自动化。

从你的账本触发警报

脚本化工作流的另一个强大用途是根据财务数据中的条件设置警报。由于你的账本会定期更新(并且可以包含未来日期的项目,如即将到来的账单或预算),你可以用脚本扫描它并在重要事件发生时得到通知。

低账户余额警告

为了避免透支或维持最低余额,你可能希望在任何一个账户(例如支票账户或储蓄账户)低于阈值时收到警报。以下是如何实现这一点:

  1. 确定当前余额:通过加载器加载 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

    只传递条目:第二个参数是 min_accounts,而不是选项映射。然后提取数值(例如,balance.get_currency_units('USD') 返回 USD 中的十进制金额)。像查询聚合一样,余额会分别保留每种货币。然而,对于大多数情况,使用查询更简单。

  2. 检查阈值:将余额与你预定义的限额进行比较。如果低于限额,则触发警报。

  3. 触发通知:这可以简单到向控制台打印警告,但对于真正的警报,你可能需要发送电子邮件或推送通知。你可以通过 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 中有两种表示即将到来的义务的方法:

  • 事件:Beancount 支持用于任意日期注释的 event 指令。例如:

    2025-05-10 event "BillDue" "Mortgage payment due"

    这不会影响余额,但会记录一个带有标签的日期。脚本可以扫描 entries 中的 Event 条目,其中 Event.type == "BillDue"(或你选择的任何自定义类型),并检查日期是否在从今天起的接下来 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类。

扩展可以做几件事:

  • 钩子:它们可以挂钩到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和数据

脚本化工作流程的优势之一是能够引入外部数据。以下是常见的集成:

  • 汇率和商品:Beancount默认不会自动获取价格(以保持报告确定性),但它提供了一个_Price_指令供你提供汇率。你可以自动化获取这些价格。例如,一个脚本可以查询API(Yahoo Finance、Alpha Vantage等)获取最新的汇率或股票价格,并将价格条目追加到你的账本中:

    2025-04-30 price BTC 30000 USD
    2025-04-30 price EUR 1.10 USD

    有一些工具,比如托管引擎中由 Beanprice 支持的 bea price,可以抓取每日报价并以 Beancount 格式输出。你可以用 bea engine enable beanprice 一次性启用它,然后安排 bea price main.beancount 每晚运行来更新一个 prices.beancount 包含文件。或者使用 Python:例如,用 requests 库调用 API。Beancount 的文档建议,对于公开交易的资产,你可以“调用一些代码来下载价格并为你写出指令。” 换句话说,让脚本去查找并插入 price 行,而不是你手动来做。

  • 股票投资组合数据:类似于汇率,你可以集成 API 来获取详细的股票数据或股息。例如,Yahoo Finance API(或像 yfinance 这样的社区库)可以检索某只股票代码的历史数据。一个脚本可能会用你持有的每只股票的月度价格历史来更新你的账本,从而实现准确的历史市值报告。一些自定义扩展(如 fava_investor)甚至能即时拉取价格数据用于显示,但最简单的做法是定期将价格导入账本。

  • 银行 API(Open Banking/Plaid):与其下载 CSV,你可以使用 API 自动获取交易记录。像 Plaid 这样的服务可以聚合银行账户,并允许以编程方式访问交易。在高级设置中,你可以让一个 Python 脚本使用 Plaid 的 API 每天拉取新交易并保存到文件(或直接导入账本)。一位高级用户构建了一个系统,让 Plaid 输入到他们的导入管道中,使他们的记账几乎全自动。他们指出“没有什么能阻止你在本地注册 Plaid API 并做同样的事情”——也就是说,你可以编写本地脚本来获取银行数据,然后用你的 Beancount 导入逻辑将其解析为账本条目。某些地区有银行提供的开放银行 API,也可以类似地使用。

  • 其他 API:你还可以集成预算工具(导出计划预算,以便与 Beancount 中的实际数据进行比较),或使用 OCR API 读取收据并自动匹配到交易。由于你的脚本可以完全使用 Python 的生态,你可以集成从电子邮件服务(用于发送提醒)到 Google Sheets(例如,更新一张月度财务指标表)再到消息应用(通过 Telegram 机器人向你发送摘要报告)的一切。

使用第三方 API 时,记得保护你的凭据(使用环境变量或配置文件存储 API 密钥),并在脚本中妥善处理错误(网络问题、API 停机)。缓存数据通常是个好主意(例如,存储获取的汇率,以免重复请求相同的历史汇率)。

模块化、可维护脚本的最佳实践

在构建可脚本化的工作流时,请保持代码的组织性和健壮性:

  • 模块化:将不同关注点拆分到不同的脚本或模块中。例如,将“数据导入/对账”、“报告生成”和“提醒”分开。你甚至可以为你的账本创建一个小型 Python 包,包含像 ledger_import.pyledger_reports.py 等模块。这使每个部分更容易理解和测试。

  • 配置:避免硬编码值。使用配置文件或脚本顶部的变量来设置账户名称、阈值、API 密钥、日期范围等内容。这样可以轻松调整,而无需深入修改代码。例如,在顶部定义 LOW_BALANCE_THRESHOLDS = {"Assets:Bank:Checking": 500, "Assets:Savings": 1000},然后你的提醒脚本就可以遍历这个字典。

  • 测试:将你的财务自动化视为关键任务代码——因为它确实是!为复杂逻辑编写测试。Beancount 提供了一些测试辅助工具(内部用于导入器测试),你可以利用它们模拟账本输入。即使没有花哨的框架,你也可以准备一个虚拟 CSV 和预期输出交易,并断言你的导入脚本能产生正确的条目。如果你使用 pytest,你可以轻松集成这些测试(正如 Alex Watt 通过 just test 命令包装 pytest 所做的那样)。

  • 版本控制:将你的账本和脚本置于版本控制(git)之下。这不仅为你提供备份和历史记录,还能鼓励你以受控的方式进行更改。可以为你的“财务脚本”打上版本标签,或在调试问题时查看差异。有些用户甚至将财务记录放在Git中,以便查看随时间的变化。但要注意忽略敏感数据(如原始账单文件或API密钥),不要将它们提交到仓库中。

  • 文档化:为未来的你记录你的自定义工作流程。在仓库中写一个README,说明如何设置环境、如何运行每个脚本以及每个脚本的功能,这在几个月后会非常宝贵。同时,为代码添加注释,尤其是任何不明显的会计逻辑或API交互。

  • Fava插件维护:如果你编写Fava扩展,请保持简单。Fava可能会发生变化,因此功能明确的小型扩展更容易更新。避免重复过多逻辑——尽可能使用Beancount的查询引擎或现有的辅助函数,而不是硬编码可能对账本变化敏感的计算。

  • 安全性:由于你的脚本可能处理敏感数据并连接外部服务,请谨慎对待。不要暴露API密钥,并考虑在安全的机器上运行自动化。如果你使用托管解决方案或云服务(如调度GitHub Actions或运行Fava的服务器),请确保账本数据在静态时加密,并且你对隐私影响感到满意。

遵循这些实践,你可以确保工作流程保持可靠,即使你的财务状况(以及工具本身)发生变化。你希望脚本可以年复一年地使用,只需极少的调整。

结论

Beancount和Fava为精通技术的用户提供了一个强大而灵活的平台,可以完全自定义个人财务跟踪。通过编写Python脚本,你可以自动化如对账等繁琐的任务,生成满足你需求的丰富报告,并通过及时的提醒掌握财务动态。我们涵盖了一系列从基础到高级的示例——从简单的查询和CSV导入开始,再到完整的Fava插件和外部API集成。在实现这些时,从简单开始,逐步构建。即使是几个小的自动化脚本也能节省数小时的工作,并大幅提高准确性。请记住,因为一切都是纯文本和Python,你完全掌控——你的财务系统与你一同成长,适应你的特定需求。祝你脚本编写愉快!

来源: 上述技术源自Beancount文档和社区经验。如需进一步阅读,请参阅Beancount的官方文档、社区指南和博客,以及Awesome Beancount仓库,了解有用的插件和工具链接。

来源:https://beancount.io/zh/docs/Solutions/scriptable-workflows