跳转到主要内容

Beancount是什么:纯文本复式记账

Beancount是面向股票、加密货币和日常账目的纯文本复式记账——它是什么,以及为什么工程师使用它。

本指南综合了使用强大、开源、纯文本记账工具 Beancount 进行纯文本记账的最佳实践。它涵盖了基础理念、从基础到进阶的语法、复杂资产的实际案例,以及长期账本管理策略。

第一部分:"为什么" —— 智能簿记的基础​

在深入了解“怎么做”之前,理解“为什么”至关重要。有效的记账是个人财务管理的基石,也是通往财务清晰与自由的必经之路。

超越支出追踪:通往财务清晰之路​

简单的支出追踪应用只会告诉你钱花到哪里去了。而一套强大的记账系统不仅能告诉你这些,还能提供更多:它提供你财务健康状况的完整图景,包括你的净资产、现金流以及投资表现。首要目标是可观测性——获得对财务生活清晰、数据驱动的理解,这使你能够做出明智决策、评估风险,并朝着退休等长期目标努力。

为什么要复式记账?平衡系统的力量​

与单式记账(简单的支出清单)不同,复式记账法将每笔交易记录为至少两个账户之间的价值流动。其核心原则是基本会计恒等式:

资产=负债+权益(净资产)\text{资产} = \text{负债} + \text{权益(净资产)}

这套系统确保你的账本始终保持平衡,大幅减少错误。它通过生成资产负债表(你拥有什么、欠什么)和利润表(你赚了多少、花了多少)等关键报表,提供完整的财务图景。

第二部分:Beancount 入门​

Beancount 是一个强大的、基于 Python 的纯文本记账工具。

在 Beancount.io 上首次体验 Fava​

Beancount.io 提供了一个强大的环境,将 Beancount 引擎与移动应用(iOS、Android)和网页应用——Fava(一个出色的基于网页的账本可视化界面)结合在一起。无需安装。当你使用该平台时,你直接在一个文本编辑器中操作你的账本文件(例如 main.bean),并实时看到 Fava 生成的报表更新。

Fava 将你写的纯文本转化为交互式图表、财务报表和可筛选的交易列表,让你清晰地查看财务报告。

或从终端开始使用bea​

如果你更愿意在 shell 中工作,bea 就是 Beancount.io 的命令行工具。无需账户:用 brew install bex-co/tap/bea 或 uv tool install beancount-io 安装,它就可以在同一个纯文本文件上创建、校验、查询和生成报表。CLI 快速入门能让你在几分钟内得到第一本账本,而用 bea 度过第一个月会端到端地完成一整个月。

五种核心账户类型​

Beancount 使用五种顶层账户类型,它们构成了你账本的结构:

账户类型描述典型余额
Assets你拥有的东西(现金、银行账户、投资、房产)。正数
Liabilities你欠的东西(信用卡债务、贷款、房贷)。负数
Income钱的来源(工资、奖金、利息)。负数
Expenses钱的去向(餐饮、房租、旅行、税费)。正数
Equity你的净资产;用于初始余额。负数

Beancount 等式​

Beancount 强制执行它自己版本的会计恒等式,即整个账本中所有分录之和等于零:

资产+支出+负债+收入+权益=0\text{资产} + \text{支出} + \text{负债} + \text{收入} + \text{权益} = 0

这就是为什么按照惯例,Income、Liabilities 和 Equity 账户持有负值——它们是增加你 Assets 和 Expenses 的资金_来源_。

第三部分:Beancount 的语言 —— 核心语法​

Beancount 中的所有条目都是以日期开头的指令。

定义账户(open)和商品​

在你使用一个账户之前,必须用 open 指令声明它。你还可以选择性地指定它将持有的货币或“商品”。

; YYYY-MM-DD open Account:Name [Commodity1, Commodity2, ...]
2020-01-01 open Assets:Bank:US:Chase:Checking      USD
2020-01-01 open Liabilities:CreditCard:US:Discover USD
2020-01-01 open Expenses:Food:Groceries
2020-01-01 open Income:Salary:Google

商品可以是现实世界的货币(例如 USD、JPY),也可以是你定义的任何自定义单位,比如航空里程(MILES_UA)或股票代码(HOOL)。

记录你的第一笔交易(*)​

交易是最常见的条目。它们以日期、标志(* 表示完整交易,! 表示未完成交易)、可选的收款方和描述开头。紧随其后的每一行(缩进两个空格)都是对一个账户的“分录”。

; YYYY-MM-DD * "Payee" "Description"
;   Account1      Amount Commodity
;   Account2     -Amount Commodity
 
2024-07-28 * "Trader Joe's" "Weekly groceries"
  Expenses:Food:Groceries     125.50 USD
  Liabilities:CreditCard:US:Discover  -125.50 USD

为方便起见,如果一笔交易只有两条分录,你可以省略第二行的金额,Beancount 会自动计算。

2024-07-28 * "Trader Joe's" "Weekly groceries"
  Expenses:Food:Groceries     125.50 USD
  Liabilities:CreditCard:US:Discover

交易级平衡: 更重要的是对于日常使用而言,每一笔单独的交易也必须平衡——单笔交易内所有分录之和必须等于零。如果一笔交易不平衡,Beancount 会显示这样的错误:

Beancount 错误提示

处理多货币交易(@ 和 @@)​

Beancount 擅长多币种记账。

  • 用 @ 指定每单位换算价格。
  • 用 @@ 指定换算的总成本。
; Buying a flight in EUR with a USD card
2024-08-01 * "Lufthansa" "Flight to Berlin"
  Expenses:Travel:Flights      500.00 EUR @@ 545.00 USD  ; 500 EUR cost me 545 USD in total
  Liabilities:CreditCard:US:Discover  -545.00 USD

第四部分:确保准确性 —— 对账的艺术​

维护准确账本的关键实践是定期对账。这涉及将你 Beancount 账本中的余额与金融机构提供的官方对账单进行比对。

使用余额断言(balance)自动化检查​

balance 指令是你进行自动化检查的主要工具。你断言在某个给定日期,某个账户有特定余额。如果 Beancount 计算出的余额与你的断言不符,它会报错。这对于快速定位错误极为宝贵。

注意: 余额断言检查的是指定日期_开始时_(该日任何交易之前)的账户状态。

; From your monthly credit card statement
2024-08-01 balance Liabilities:CreditCard:US:Discover  -1432.78 USD

关联支持文件(document)​

你可以链接到银行对账单或收据等外部文件,形成可审计的轨迹。Fava 让这些链接可点击。

2024-08-01 document Liabilities:CreditCard:US:Discover "statements/discover-2024-07.pdf"

纠正错误和初始化余额​

当你开始记账或发现无法追溯的差异时,你需要做一个调整。标准做法是使用一个特殊的 Equity 账户。

; Initializing a cash account when starting your ledger
2020-01-01 * "Initial Balance" "Setting up cash account"
  Assets:Cash:Wallet           200.00 USD
  Equity:Opening-Balances     -200.00 USD

Equity:Opening-Balances 账户持有从未知或外部来源进入你账本的金额。

对于确切差异并不重要的快速修复,pad 指令可以自动调整账户余额以符合后续的 balance 断言,将差额记入权益账户。请谨慎使用,因为它可能掩盖更大的问题。显式调整通常更安全。

第五部分:高级和现实交易模式​

追踪债务:管理应收和应付​

复式记账非常适合追踪别人欠你的钱(Assets:Receivables)或你欠别人的钱(Liabilities:Payable)。

示例: 你为一顿 90 美元的聚餐买单,你的朋友 Bob 欠你他那 45 美元的份额。

  1. 记录初始支出和应收款:

    2024-08-05 * "Dinner Place" "Dinner with Bob"
      Expenses:Food:Restaurant          45.00 USD  ; Your share
      Assets:Receivables:Bob            45.00 USD  ; Bob owes you
      Assets:Bank:US:Chase:Checking    -90.00 USD
  2. 当 Bob 还你钱时:

    2024-08-06 * "Bob" "Paid me back for dinner"
      Assets:Bank:US:Chase:Checking     45.00 USD
      Assets:Receivables:Bob           -45.00 USD

Assets:Receivables:Bob 账户现在为零,你的账本完美平衡。

资产与费用:购车与折旧​

像汽车这样的大额购买不是简单的支出;它是购置一项会随时间贬值(折旧)的资产。

  1. 将购买记录为资产:

    2023-01-15 * "Toyota Dealer" "Purchase of a new car"
      Assets:Car:ToyotaCamry         30000.00 USD
      Assets:Bank:US:Chase:Checking -30000.00 USD
  2. 记录年度折旧: 假设你估计这辆车每年贬值 3,000 美元。在年末,你将此记录为支出。

    2023-12-31 * "Depreciation" "Annual car value depreciation"
      Expenses:Depreciation:Car      3000.00 USD
      Assets:Car:ToyotaCamry        -3000.00 USD

在这笔分录之后,你的 Assets:Car:ToyotaCamry 账户正确地反映了汽车的新价值(27,000 美元),并且你已将该年的使用成本恰当地作为支出入账。

第六部分:深入探讨 —— 建模复杂的现实世界资产​

案例研究 1:房地产会计​

房子往往是你最大的资产和负债。以下是如何为它建模。

  1. 创建账户和自定义商品:

    2022-01-01 commodity HOUSE_123MAIN
      name: "Property at 123 Main St"
    2022-01-01 open Assets:Property:Home:123Main        HOUSE_123MAIN
    2022-01-01 open Liabilities:Mortgage:HomeLoan       USD
    2022-01-01 open Expenses:Home:Interest
    2022-01-01 open Expenses:Home:PropertyTax
  2. 记录购买: 假设你以 10 万美元首付和 40 万美元贷款买下一套 50 万美元的房子。

    2022-03-15 * "Settlement Company" "Purchase of 123 Main St"
      Assets:Property:Home:123Main          1 HOUSE_123MAIN {500000.00 USD}
      Assets:Bank:DownPayment          -100000.00 USD
      Liabilities:Mortgage:HomeLoan    -400000.00 USD
  3. 记录每月房贷还款: 你的每月还款由本金(减少负债)和利息(一项支出)组成。

    2022-04-01 * "Mortgage Bank" "Monthly Mortgage Payment"
      Liabilities:Mortgage:HomeLoan      800.00 USD   ; Principal
      Expenses:Home:Interest            1200.00 USD   ; Interest
      Assets:Bank:US:Chase:Checking    -2000.00 USD
  4. 追踪增值(未实现收益): 房子的市值会变化。为了在不影响你官方净资产的情况下追踪它(因为在你卖出之前收益并未实现),你可以使用一个带有“虚拟”货币的价格指令。

    对于受支持的市场资产和货币对,实时价格可以在托管账本中维护估值报价。单项房产仍然需要你自己的估价和有日期的价格,如下例所示。

    ; The purchase price is the real cost basis
    2022-03-15 price HOUSE_123MAIN   500000.00 USD
     
    ; An updated market estimate is an unrealized gain
    2024-01-01 price HOUSE_123MAIN   550000.00 USD.UNREALIZED

这让你可以在 Fava 的图表中看到估计价值,而不会不当抬高你的资产负债表。

探索一个带房贷追踪、折旧和房产出售的实时房地产示例账本:

在新标签页中打开 房地产示例账本

案例研究 2:跟踪限制性股票单位(RSUs)​

RSU 是一种常见的股权薪酬形式。为它们记账涉及追踪初始授予、归属事件和代扣税。

  1. 初始设置: 为已归属(HOOL)和未归属(HOOL.UNVEST)股票创建商品,以及后面分录涉及的每一个账户。预先声明所有这些账户,正是让下面两笔交易能够独立加载的原因。

    2021-01-01 commodity HOOL
    2021-01-01 commodity HOOL.UNVEST
    2021-01-01 open Assets:Brokerage:Etrade:HOOL          HOOL
    2021-01-01 open Assets:Grant:Unvested                 HOOL.UNVEST
    2021-01-01 open Income:Grant:Awards                   HOOL.UNVEST
    2021-01-01 open Expenses:Grant:Vested                 HOOL.UNVEST
    2021-01-01 open Income:Salary:Hooli:RSU               USD
    2021-01-01 open Expenses:Taxes:Federal                USD
    2021-01-01 open Expenses:Taxes:State                  USD
  2. 记录初始授予: 这笔交易展示了总授予量进入一个未归属资产账户。此时尚无应税事项,因此整个条目以追踪商品 HOOL.UNVEST 计价。

    2021-02-01 * "Hooli" "Initial RSU Grant"
      Assets:Grant:Unvested        1000 HOOL.UNVEST
      Income:Grant:Awards         -1000 HOOL.UNVEST
  3. 记录一次归属事件: 这是关键交易。当股票归属时,你确认收入,雇主代扣股票以缴税,你保留其余部分。假设 100 股以 150.00 美元归属,那么:

    • 总薪酬:100 × 150.00 美元 = 15,000.00 美元
    • 为缴税代扣的股票:40 × 150.00 美元 = 6,000.00 美元(联邦 4,800.00 美元 + 州 1,200.00 美元)
    • 你保留的股票:60 × 150.00 美元 = 9,000.00 美元的成本基础
    • 到手现金:0.00 美元——全部代扣都以股票支付

    这些金额就是该分录的内容,并且它们对得上:6,000.00 美元的税加上 9,000.00 美元的留存股票,正好等于 15,000.00 美元的收入。

    2022-02-01 * "Hooli" "RSU Vesting Event"
      ; Ordinary income: 100 shares * $150.00
      Income:Salary:Hooli:RSU                   -15000.00 USD
      ; Tax withheld, funded by 40 of the 100 shares (40 * $150.00 = $6,000.00)
      Expenses:Taxes:Federal                      4800.00 USD
      Expenses:Taxes:State                        1200.00 USD
      ; The 60 shares you keep, at their $150.00 vesting-day basis
      Assets:Brokerage:Etrade:HOOL                  60 HOOL {150.00 USD}
      ; Retire the 100 unvested units the grant was tracking
      Assets:Grant:Unvested                       -100 HOOL.UNVEST
      Expenses:Grant:Vested                        100 HOOL.UNVEST

这笔单一、平衡的交易为整个事件建模:未归属授予减少,收入被确认,税被代扣,60 股净归属股票以正确的成本基础出现在你的券商账户中,供未来计算资本利得时使用。

有两个细节值得抄进你自己的账本。第一,按账户逐笔写出代扣,而不是让一个包罗万象的平衡账户吸收它——如果联邦和州的分录之和不等于被代扣股票的价值,这笔交易将不平衡,Beancount 会告诉你。第二,不要在分录之间留空行:空行会终止交易,Beancount 会对随后的分录报语法错误。

如果你雇主在公开市场上卖出代扣的股票,而不是以归属价收回它们,那么这次卖出是另一笔单独的交易——Assets:Brokerage:Etrade:HOOL 的减少对应现金——卖出价与 150.00 美元基础之间的任何差额都是一笔小的资本利得或损失。把它排除在归属分录之外。

第七部分:账本项目管理​

随着你的账本增长,组织变得至关重要。

使用版本控制(Git)保护你的数据​

由于你的账本是文本文件,它非常适合用 Git 进行版本控制。这为你提供了所有变更的完整历史,保护你免受意外删除或错误的影响。警告: 你的财务数据高度敏感。请使用 GitHub/GitLab 等服务上的私有仓库,或自行托管。

使用标签(#)和链接(^)组织​

Beancount 提供了除账户之外对交易进行分组的两种方式:

  • 标签(#): 用于事件或项目。例如,你可以筛选与某次特定旅行相关的所有交易。 2024-07-20 * "Hotel" "Vienna" #trip-europe-2024
  • 链接(^): 用于连接发生在不同时间的、财务上相关的交易,比如一次现金取款和相关的银行手续费。

结构化文件的可扩展策略(include)​

单一庞大的文件难以管理。使用 include 指令将账本拆分为多个文件。 main.bean:

; Main ledger file
 
; Global options
option "title" "My Personal Ledger"
option "operating_currency" "USD"
 
; Include account declarations and other files
include "accounts.bean"
include "years/2023.bean"
include "years/2024.bean"
include "events/trip-europe-2024.bean"

一个稳健的组织策略,按优先级排序:

  1. 按事件: 为一次重大的、自成一体的事件创建一个单独文件(例如 trip-europe-2024.bean)。
  2. 按类别/收款方: 对于高度规律、重复性的交易,如水电费或工资,将它们归入自己的文件(例如 recurring-rent.bean)。
  3. 按账户: 对于与特定账户紧密耦合的交易(利息、手续费、信用卡还款),可以考虑按账户单独建文件。
  4. 按日期: 对于所有其他一般交易,简单按年(2024.bean)或按月(2024/07.bean)拆分就很有效。

第八部分:结论​

Beancount 的学习曲线很陡峭,但它以对你财务数据无与伦比的掌控力、灵活性和控制力回报你的付出。通过接纳复式记账的原则以及 Beancount 提供的实用工具,你可以从简单的支出追踪迈向一套完整、准确、有洞察力的个人财务管理系统。你的账本将成为一项永久、私密且极其宝贵的资产,帮助你理解过去、规划未来。

Beancount.io 入门​

Beancount.io 是一个现代化的基于云的财务管理平台,它将你基于文本的交易记录转化为全面的财务报表,包括利润表、资产负债表和试算平衡表。通过将纯文本文件的可靠性与强大的可视化工具相结合,Beancount.io 帮助你在对财务生活保持精确控制的同时,获得对投资表现的宝贵洞察。

用 Beancount.io 开启你的财务之旅——查看当前方案和定价了解各档位包含的内容。

beancount.io 账本仪表盘,显示净资产趋势图、账户余额和一个 AI 助手栏

探索实时账本 →

beancount.io 上 Apple 公开 Beancount 账本的利润表,含净利润图表和收入与支出明细

探索实时账本 →

beancount.io 上 Apple 公开 Beancount 账本的资产负债表,含净资产图表和资产与负债明细

探索实时账本 →

来源:https://beancount.io/zh/docs/introduction-to-beancount