财富编织:Beancount复式记账指南
财富编织:Beancount复式记账指南
蔡坨坨转载请注明出处❤️
作者:测试蔡坨坨
原文链接:caituotuo.top/6a84b0ca.html
前言
你好,我是测试蔡坨坨。
在前面两篇文章中,我们探讨了「一年之余,财富何方?」以及「财富梳理:复式记账之道」,旨在回答两个核心问题:“为什么要记账?”和“如何科学记账?”。
实践是检验真理的唯一标准。同样,复式记账也需要通过实践来理解。在开始实践之前,学习很多晦涩难懂的会计学概念没有任何意义。正如上篇文章末尾所述,对于复式记账的理论部分,我们只需记住会计恒等式
和两句口诀
,便能游刃有余。
1 | 会计恒等式:资产+费用=负债+所有者权益+收入 |
文本记账:Beancount
要开始实践,就要有工具上手,复式记账是方法论,而Beancount则是支持复式记账的工具。
在众多选择中,我推荐使用Beancount。虽然第一次见到Beancount时,我的内心是拒绝的,纯文本环境,也没有市面上大多数记账软件方便快捷、界面简单的优势,似乎还需要一点学习成本。
但是,在犹豫片刻之后,我便鼓起勇气用一晚上时间完成了学习和初始化,两天后就决定全面搬迁到Beancount。从2024年初开始,到现在已有三个多月的使用体验,我对它十分满意,希望能将它推广给更多的用户,原因如下:
- 开源免费:Beancount是一个开源的Python库,可以在本地运行,同时还有活跃的社区支持和持续的更新和改进,适合计算机宝宝
- 文本格式:账本使用简洁的文本格式进行记账,方便存储和管理,个人拥有全部的数据,还可以使用Git管理,记账时只需打开文本文件,无需等待图形界面慢悠悠的启动
- 灵活性:账本的语法很规范,也具备灵活性,像编程语言一样可以嵌套引入,配合插件可实现语法高亮和代码检查,用户能够根据自己的需求和偏好自定义账户结构和记账方式
- 数据可视化:除了完整的命令行工具链,官方还提供了可视化工具Fava,还有基于类SQL的查询和报表生成
- 适应性:没有预先定义的类别、货币等现实世界的概念,可以轻松实现多币种记账,包括各种点数、虚拟货币等
除了Beancount之外,还有许多其他开源的工具可供选择。可以参考Plain Text Accounting网站上列出的,类似的具有竞争力的还有Ledger和hledger。其中,Ledger是这类工具的开创者。无论是里面介绍的哪个工具,实现理念都是大同小异,即记录账户之间的资金流动
。
为什么选择文本记账?
Beancount是一个基于文本的复式记账软件,与其说是记账软件,不如说它是复式算账软件,因为它没有提供任何对于记账相关的功能,它提供的是对于某种特定格式的账本的解析功能,实际上的记账人是你,甚至你的账本Beancount不会做任何操作,官方将其定义为“一种复式记账计算机语言
”。
复式记账软件不少,就算是开源世界,也有GnuCash,这些软件都有完整的GUI操作,用户在一堆文本框里输入各种数字和文字,软件接收输入后存储到自己的数据库里,又何苦选择文本记账的方式呢?
我给出的理由是:
- 大多数GUI记账软件的数据都是存储在三方,无法直接看到或操作他们的数据,必须通过软件来操作
- 文本记账的折腾成本低,比如GnuCash,如果你要给它折腾一个手机、PC的多端记账方案,你就得研究它的数据库和接口,而文本记账,只用打印文本就行了
- 一旦软件停止更新,用户的数据就危在旦夕,难以导出和复用,很难跨平台或跨设备同步
为什么选择Beancount?
上面提到,文本记账的话,其实还有Ledger和hledger可以选择。Beancount是一个Ledger-like软件,Ledger是这一类复式记账软件的开创者。相比于C++写的Ledger,用Python写的Beancount更轻便,方便增加插件和二次开发,同时也增加了许多功能,如灵活强大的“货币”支持(Beancount其实并不知道什么是货币,它记录的只是通货(commodity)
的变化,所有的通货皆由用户自定义,因此Beancount可以用来记录除货币在内的任何东西的变化,比如假期天数、信用卡积分、股票、基金、航空里程等,当然也可以用来数豆子,这也是Beancount名字的来源)。
在这三个软件中,Beancount的生态社区最为活跃。它简化了记账符号的使用,将正负号代替了传统的“借”和“贷”。
此外,Beancount官方提供了一个名为fava的图形化管理工具。通过fava,用户可以在web界面中展示文本账簿,使记账信息一目了然。fava还提供各种报表统计分析功能,让用户能够更加方便地进行财务分析。可以访问官方提供的Demo提前体验一波。
安装Beancount和Fava
由于Beancount和Fava都是使用Python实现的,因此安装过程也非常简单(前提是需要有Python环境)。
安装Beancount
1
pip install beancount
安装Fava
1
pip install fava
安装完成后,您只需执行
fava xxx.bean
即可查看任意账本文件(鉴于目前还没开始编写账本,可以先用fava提供的示例账本过把干瘾)1
fava example.bean
打开浏览器,并访问 http://127.0.0.1:5000,即可开始使用
损益表和资产负债表
运行Fava后,你会看到一个华丽的 Web UI,其中有两张报表值得我们关注,损益表和资产负债表。
记账的目的是为了了解自己的财务状况,换成通俗易懂的话就是为了回答以下三个问题:
- 我的钱从哪里来?
- 我的钱在哪?
- 我的钱去哪了?
一本维护良好的账本能生成很多有用的财务报表,而其中最有用的是损益表
和资产负债表
,前者能回答第一个和第三个问题,后者能回答第二个问题。
损益表(income statement)
损益表,也称为利润表,在这张报表中,我们可以一目了然地看到自己每月有哪些收入、收入来自于哪些地方、有多少支出、支出花在了什么地方。
在复式记账中,支出类借增贷减,上半部分代表支出(正数、流出),收入类借减贷增,下半部分代表收入(负数、流入)。
鼠标悬停在某个月份上,可以看到这个月份的净利润:
右上角可以切换 按年/按季/按月/按周/按日
展示:
还可以切换到收入
或支出
上,只看收入或支出的情况:
默认展示堆叠条形图,鼠标悬停在条形图上,可以看到此项在这个月的变化:
切换到Income或Expenses上,能看到收入或支出的旭日图:
页面右上角有时间、账户、筛选(标签,收款人等等)几个过滤框,日期除了支持具体的年月日,还支持 year/month/day
等关键字,例如 year-2 - year
表示只展示前年到今年的交易:
页面下半部分是具体每个账户的收支情况:
点击某个账户,可以看到这个账户的曲线图和具体的交易记录:
资产负债表(balance sheet)
在资产负债表里,可以一目了然地看到自己有多少资产、资产分别在哪些账户里,有多少负债、是哪里的负债。
上半部分是净资产变化的折线图,下半部分是具体每个账户的情况。
切换到Assets、Liabilities或Equity上,可以看到资产、负债或净资产的饼图:
快速入门
说了这么多,也许你已经安装完毕,磨拳搓掌准备行动。
Beancount的使用很简单,概括为两个步骤:
- 使用
文本文件
(文件扩展名为.bean
)按一定格式记账 - 命令行执行
fava xxx.bean
下面是个完整的示例:
账本文件中包含账本信息设置、账户设置、账户初始化、交易记录等步骤,我们将在下文做详细说明。
1 | ;【一、账本信息】 |
执行命令:
1 | fava demo.bean |
到这里,我们已经完成了Beancount的快速入门。
最佳实践
1. 编辑器支持
Beancount的作者是Emacs的用户,因此自己写了Emacs的插件。Vim的用户可以使用第三方提供的nathangrigg/vim-beancount插件。Sublime的用户可以使用sublime-beancount。
而我使用的是Visual Studio Code,配合Beancount(by Lencerf)扩展插件,能够实现语法着色、账户自动补全、数字按小数点对齐、错误提示等功能。
选择编辑器和插件的目的在于提高效率,使记账变得更加轻松和愉快。
当然,如果你不嫌麻烦,直接使用最原始的记事本也完全没有问题。
2. 账本结构
在组织账本时,将其拆分成多个文件是一个很好的做法。这样可以更好地管理和维护账本,使其更具可读性和可维护性。通过使用Beancount的include
语法,可以将多个账本文件关联起来,形成一个完整的账户体系。
例如,我的账本结构目录如下:
最外层是main.bean
主账本文件,用于整合和管理所有其他账本文件,查账时只需执行命令fava main.bean
;开户和账户余额初始化相关的信息放在accounts
文件夹下,并使用assets、liabilities等关键字命名文件,以便对应相应类型的账户;交易记录按照年份进行分类,在每个年份文件夹中可以进一步细分,例如日常支出、转账、收入、旅游娱乐、投资收益等。
这样的结构设计使得账本信息更加有条理,便于查找和管理。同时,也方便了账本的维护和更新,能够更有效地记录和追踪财务情况。
3. 账本设置
在初始化时,需要配置账本名称、主货币等属性。
1 | ;【账本设置】 |
4. 账户相关
在开始记账之前,必须开通好账户。
在Beancount中,账户是以树形结构组织的,支持多层级,以英文冒号:
分隔,如Assets:Bank:CMB:1234
,第一层必须是会计恒等式中的五个账户类型之一。
命名时可以采用银行的缩写、尾号、用途等信息,以便于识别和管理。
开通账户
开户命令:
1 | 日期 open 账户名 货币类型 |
举栗:
1 | 2024-01-01 open Assets:Wechat:MiniFund CNY ;微信零钱通 |
- 开户日期:格式为
yyyy-MM-dd
,开户日期需要在该账户第一笔交易之前,可以使用开始复式记账的前一天或者自己的生日。当然还有一些更有创意的选择:- Assets 和 Liabilities 账户中的借记卡和信用卡,可以以在银行的开户日期作为 Beancount 中的开户日期
- Expenses 账户可以使用自己的生日作为开户日期
- Income 账户下可以按来源分类,如 Income:Salary 以公司入职时间作为开户日期
- open:开户命令关键字。
- 账户名:账户可以使用英文分号
:
分割成多个层级,合理利用层级可以让账户层次更清晰;第一层级只能使用Assets、Liabilities、Equity、Income、Expenses中的一个,因为Beancount需要根据这个字段区分账户的性质,以决定它们该出现在哪张报表中;第二层级必须以大写英文或数字开头,后面的层级就没有做限制了。 - 货币类型:开户时货币类型不是必须的,但建议加上,记录交易时货币不一致会报错,货币可设置多个,用英文逗号
,
分隔。 - 注释:beancount语法使用英文分号
;
作为注释符号。
日常交易中涉及的账户,一定可以归于其中某一类。
合理开户的核心要素在于你想要追踪哪些东西
,比如你想看每个月吃了多少肯德基,你就可以专门开个费用账户叫做Expenses:KFC
,如果你完全不在意三餐的区别,开一个吃饭
(Expenses:Food)账户就足够了。
账户类型 | 说明 | 举栗 |
---|---|---|
资产(Assets) | 所有我的 | 流动资产:银行储蓄卡余额、微信零钱、零钱通、支付宝余额、余额宝等;固定资产:房子、车子等 |
费用(Expenses) | 花出去的 | 衣、食、住、行、娱乐、旅游等 |
负债(Liabilities) | 问别人借的 | 信用卡CreditCard、贷款Loan、花呗、京东白条、XX月付、房贷、车贷、借钱Payable等 |
所有者权益(Equity) | 老板投的钱 | 这个账户比较特殊,在账户初始化、误差处理等少数场合使用 |
收入(Income) | 赚的钱 | 工资Salary、投资收益Investments、副业收入Sideline、意外收入Gift、二手交易2ndTrade等 |
开户示例:
1 | ;资产账户 |
账户余额初始化
当我们开始记账时,一般资产和负债都不会是0,所以需要对资产和负债账户进行初始化。
初始化的命令格式和交易记录的命令一致,下文将会介绍交易记录。
举栗:
1 | 2024-01-02 * "账户余额初始化" "招商银行卡1234" |
注销账户
不要惧怕开户,对于一些短时间用的小账户,也可以开户,因为账户是可以关闭的,关闭后的账户不会出现在报表中,因此也不会触发你的强迫症。
销户命令:
1 | 日期 close 账户名 |
举栗:
1 | 1999-07-01 close Assets:Bank:CMB:1234 |
5. 交易记录
Beancount的交易记录主要包含日期和相关账户的资金流动(至少两个账户,可以涉及多个账户)。
命令格式:
1 | 日期 对账标志位 "交易方" "交易备注" |
举栗:
1 | ;借方:费用增加,贷方:资产减少 |
其中:
*
号表示这笔交易是确定的,没有疑问,若是!
则表示存疑,但一般用不上交易方和交易备注均可省略
货币必须与账本设置的主货币一致,若不一致会报错,如果是不同货币,可以使用
@@
进行货币转换,比如:1
2
32024-02-26 * "XXX" "转账备注:微信转账(兑换2500泰铢)"
Expenses:Travel 2500.00 THB @@ 511.00 CNY
Assets:Wechat:MiniFund -511.00 CNY正号表示借方(正号可以省略),负号表示贷方
1
2
32024-03-09 * "高德打车" "高德地图打车订单"
Expenses:Transport 84.68 CNY ;借方:交通费用增加
Assets:Bank:CMB:1234 -84.68 CNY ;贷方:资产减少支持单个账户的自动补全
1
2
3
42024-03-01 * "KFC" "10块钱买了一个吮指原味鸡"
Expenses:Food ;自动补全,会转化为 Food账户 +10.00 CNY
Assets:Bank:CMB:1234 -7.00 CNY
Equity:Tickets:KFC -3.00 CNY ;假设有个3元的优惠券打标签
单个标签
,#
后面的内容是标签名称,在Fava上可以筛选标签,查看该标签下各个账户的收支1
2
32024-03-01 * "XXX" "XXX" #Phuket
Expenses:Travel 300.00 CNY
Assets:Bank:CMB:1234 -300.00 CNY标签堆栈
,堆栈中所有交易记录都会被自动打上标签,便于后期检索1
2
3
4
5;语法:pushtag #标签内容
; 交易记录1
; 交易记录2
; .........
; poptag #标签内容1
2
3
4
5
6
7pushtag #Phuket
...
2024-03-01 * "" ""
Expenses:Travel 300.00 CNY
Assets:Bank:CMB:1234 -300.00 CNY
...
poptag #Phuket
6. 价格相关
Beancount 可以使用多种通货,但好比不同货币有汇率,这些通货之间也有对应数量关系,即某个东西值多少钱,这就是单位通货和货币的汇率。
1 | ;功能:导入某个通货某日的价格 |
7. bean-query复杂查询
Fava的华丽 Web UI 已经能展现很多有用的财务报表,满足大部分用户的需求,如果用户需要进行一些更复杂的数据统计,比如「我 2023 年吃过的饭店按次数排列」,则可以使用 bean-query 工具用 SQL 语句进行查询,详见 Beancount 作者的文档:Beancount – Query Language。
这是一个用来统计光顾麦当劳次数的例子:
8. 事件
在生活中有些事件希望被记录,但这些事情可能与财务无直接关系,Beancount也支持记录,比如旅行、会议、生日、纪念日等。
命令:
1 | 日期 even "事件分类" "事件详情" |
举栗:
1 | 2024-01-01 event "beancount" "开始使用beancount复式记账啦" |
对于大额的转账类收入或支出,如果直接归到收入或支出,会导致统计图的比例被挤压。
这种情况也可以使用事件来解决,比如创建一个Equity:Exchange
账户负责转账记录,然后在事件中创建转账条目用以记录。
1 | 2024-02-12 * "XX转账5万元" |
9. 误差处理
金额浮点数误差
在使用一段时间后,发现Beancount在金额计算上因为浮点数非精确计算的性质,会出现0.01误差(在记录金额时建议保留两位小数,比如100.00,可以消除浮点误差)。如果已经有很多的金额没有保留两位小数,出现误差也不要慌,我的解决方案是创建一个Equity:Balance-Error
账户,定期对误差进行消除。
1 | 2024-03-21 * "消除账户浮点误差" |
还有一些时间久了无法回溯的差额、四舍五入的误差,都可以使用Equity账户来处理。
解决方案是设置四个Equity账户用于处理不同场景的误差:
1 | Equity:OpenBalance 用于初始化 |
10. 定期断言
一本维护良好的账本应该定期做断言(assertion)
。
前面说到复式记账最大的优势是不仅仅记录收入和支出,同时还会记录资产负债,Beancount中可以通过balance
命令实现账户核对,标记某个日期某个账户(资产Assets账户或负债账户Liabilities)里还有多少豆子。
1 | ;资产 |
断言语句告诉 Beancount,某个账户在某个日期凌晨 00:00:00 时间点(也就是前一天深夜 23:59:59 的下一秒),余额为某个数字。如果历史交易记录无误,计算出来的金额就会与预期一致,若不一致,则Beancount就会报错提示。
Beancount 的时间精度是日
,所以诸如 open, close, balance 等带日期的语句,均发生在当日的第一笔交易之前,你可以想像它们都是在凌晨 00:00:00 时间点发生的,而普通的交易都是发生在白天。因此,要断言一月份的余额,日期应写作 02-01 而不是 01-31。
理想情况下,进行断言的频率应该是每月或每半个月一次,这样可以及时发现并纠正可能存在的错误。
综述
说到这里,我们已经介绍了Beancount的基本使用方法。如果你想了解更多关于Beancount的语法规则和更广泛的应用场景,建议参考官方文档:
- Beancount – Language Syntax:https://docs.google.com/document/d/1wAMVrKIA2qtRGmoVDSUBJGmYZSygUaR0uOMW1GV3YE0
- Beancount – Getting Started:https://docs.google.com/document/d/1P5At-z1sP8rgwYLHso5sEy3u4rMnIUDDgob9Y_BYuWE
- Beancount – Syntax Cheat Sheet:https://docs.google.com/document/d/1M4GwF6BkcXyVVvj4yXBJMX7YFXpxlxo95W6CpU3uWVc
- Beancount – Query Language:https://docs.google.com/document/d/1s0GOZMcrKKCLlP29MD7kHO4L88evrwWdIO0p4EwRBE0
虽然我们现在可以开始进行记账了。
但是,问题来了,每次都需要手动录入交易信息,对于我这种懒人来说,想想就很痛苦。
所以,本着“能坐着就不站着,能躺着就不坐着
”的原则,自动记账
这件事势在必行。
如何自动记账?
我的方案是:
- 使用Python/Java等编程语言,实现账单(微信/支付宝账单)的自动导入和解析。
- 对于没有出现在账单中的交易,可以借助机器人(如Telegram、企业微信、钉钉)来实现
快速随时
记账
通过这种方式,我们可以实现自动或半自动记账。关于账单的自动导入和解析,将在下一篇文章中进行介绍。