用 Beancount 复式记账,Double-Entry!

目录

🤔动机

已经有很多次记账的尝试,包括各种 App 如圈子账本(现已倒闭下架)等。但有几个痛点还没法解决:

  • 大多数都是简单的记载收支,这对手握多个银行账户、信用卡等的我来说,显得不够用
  • 数据安全问题,数据在服务提供者手上,既有隐私担忧,又受制于人,谁知道什么时候就删库跑路了
  • 数据报表,大多数 App 的统计都比较垃,甚至需要 VIP 才能看

等我回过神来,大学期间的所有数据全都丢失了,毕竟好几年没用了,这通知后来才看到

等我回过神来,大学期间的所有数据全都丢失了,毕竟好几年没用了,这通知后来才看到

于是,推特上搜了一下,beancount 映入眼帘,其具有的优势为:复式记账、开源、文本式存储、生态较活跃

Plain Text Accounting portal - plaintextaccounting.org

https://github.com/siddhantgoel/awesome-beancount

📝概念

复式记账

又叫“借贷记账法”,是目前世界大部分国家(包括中国)的通用记账方法,是会计的入门基本功。“有借必有贷,借贷必相等”,这里的“借”、“贷”,纯粹就是个记账符号,跟它的汉语意思没一点关系。

是把每笔交易都记录到偶数个账户(double-entry)中的记账方法,如资产账户和费用账户,提供了可观测性。

例如:刷卡买了一部手机,花了2000元——①在“银行存款”账本上记上:减少2000元;②在“存货”账本上记上:手机,增加2000元。

Beancount 中有个恒等式:(Income + Liabilities) + (Assets + Expenses) + Equity = 0 ,表示你赚的钱(Income),加上你借来的钱(Liabilities),最终要么变成你自己的钱(Assets),要么就是花掉了(Expenses),最终得到的是个零。Equity 的作用是存放已有的「权益」。

Asset 资产(+)有形或者无形的资产,比如 现金, 银行账户, 有价证券, 车子,房子 等等
Liabilities 负债(-)各种形式的负债,房贷,车贷,信用卡债,花呗
Income 收入(-)工资,奖金,彩票中奖,出租屋收入,股息,存款利息,借贷利息 等等
Expenses 支出(+)各种花费,手续费,购物,旅行,支付的贷款利息等等
Equity 权益(-)记录账本初始化之前的净收入(income - expenses)

Beancount 是一个 Ledger-like 软件。Ledger 是这一类复式簿记软件的开创者。他们共有的特点是:

  • 采用改进的复式簿记方案(使用正负号而不是「借」和「贷」来表示账户之间的变化);
  • 使用纯文本文件作为账本,用户用文本编辑器即可记账;
  • 账本既是用户输入的文件,同时也是软件的「数据库」;
  • 软件读取账本并生成报表,账本本身也可供人类直接阅读。

Beancount 可以用来记录包括货币在内任何东西的变化,比如年假天数、股票、航空里程、信用卡积分,当然了,还可以用来数豆子。这也是 Beancount 名字的来源。

🤗实践

开启一个 beancount 项目

# 安装
pip3 install beancount fava
# 生成模板
bean-example > example.beancount
# 运行 Web UI
fava example.beancount
  • 在资产负债表(balance sheet)里,看有多少资产、资产分别在哪些账户里、有多少负债、是对哪些银行的负债。
  • 在损益表(income statement)里,看每月有哪些收入、收入来自于哪些地方、有多少支出、支出花在了什么地方。

我们后面会使用 VSCode 来记账,可简单模仿例子来建立我们自己的项目。

下面是语法速记表:

Beancount - Syntax Cheat Sheet.pdf

VSCode 插件:Beancount

该插件要求最好按照其推荐的方式组织文件:

  • .bean 的文件后缀,所有账户的 openclose 放在 main.bean
  • main.beaninclude 其他文件
  • 用 VSCode 打开所在文件夹,并设置 beancount.mainBeanFile 的全路径
BeanFolder
├── .vscode
│   └── settings.json
├── main.bean
├── before2017.bean
├── 2023-01.bean
└── 2023-02.bean
{
    "beancount.mainBeanFile": "/xxx/xxx/xxx/main.bean"
}

如果对指令的格式存在疑问,可查询语法手册:

Beancount Language Syntax - Beancount Documentation

一般而言,怎么记账,可用一张图(源自:记账及fava自建服务器指南)解释:

Untitled

指令学习

commodity

YYYY-MM-DD commodity Currency

所有的货币、金融工具、商品等用 commodity 指令定义,主要是为了定义一些元数据以供其他插件使用。

1867-07-01 commodity CAD
  name: "加元"
  asset-class: "cash"

2012-01-01 commodity HOOL
  name: "Hooli Corporation Class C Shares"
  asset-class: "stock"

open

YYYY-MM-DD open Account [ConstraintCurrency,...] ["BookingMethod"]

1970-11-05 open Equity:Opening-Balances
1970-11-05 open Assets:Bank:CN:ICBC:8043 CNY
1970-11-05 open Assets:Bank:CN:BOC:8882 CNY
1970-11-05 open Assets:Bank:CN:CMB:6783 CNY

所有的账户都得 open 指令来开。

pad

YYYY-MM-DD pad Account AccountPad

2023-01-06 pad Assets:Bank:CMB:2622 Equity:Opening-Balances
2023-01-06 balance Assets:Bank:CMB:2622  1234567.0 CNY

主要用于初始化账户,使之与 balance 断言对齐。

balance

YYYY-MM-DD balance Account Amount

2014-08-09 balance Assets:Cash     562.00 USD

断言,看某一天该账户的资产是否符合预期,如果不满足就会在 Beancount 分析时报错。

price

YYYY-MM-DD price Commodity Price

2014-07-09 price HOOL  579.18 USD

顾名思义,商品的价格都由 price 指令定义。如果开启 beancount.plugins.implicit_prices 插件,就会自动在买入时推测价格。这里没有时间概念,仅可存每日价格。

关于插件,请看:https://beancount.github.io/docs/beancount_scripting_plugins.html

txn

YYYY-MM-DD [txn|Flag] [[Payee] Narration] [Flag] Account Amount [{Cost}] [@ Price] [Flag] Account Amount [{Cost}] [@ Price] ...

2014-05-05 txn "Cafe Mogador" "Lamb tagine with wine"
  Liabilities:CreditCard:CapitalOne         -37.45 USD
  Expenses:Restaurant

2014-10-05 * "Costco" "Shopping for birthday"
  Liabilities:CreditCard:CapitalOne         -45.00          USD
  Assets:AccountsReceivable:John            ((40.00/3) + 5) USD
  Assets:AccountsReceivable:Michael         40.00/3         USD
  Expenses:Shopping

2014-04-23 * "Flight to Berlin" #berlin-trip-2014 #germany
  Expenses:Flights              -1230.27 USD
  Liabilities:CreditCard

pushtag #berlin-trip-2014

2014-04-23 * "Flight to Berlin"
  Expenses:Flights              -1230.27 USD
  Liabilities:CreditCard

poptag #berlin-trip-2014

交易指令,最常用的了,完成的交易可简写为*,而未完成需要后续修订或确认的则简写为!

  • 可添加标签:例如 #berlin-trip-2014

记录证券投资

什么是成本(cost)?当你买入一支股票的时候,每股所花的钱即是成本。

什么是价格(price)?某支股票的市场价(market price),可用 price 指令定义。

你的盈亏(PnL),就是卖出-买入。

在 Beancount 中,表示 cost 的语法是 {成本, 日期, 备注},表示 price 的语法是 @ 和 @@。例如,买入 10 股成本为 1000 USD 的 AMZN 记为:

2017-07-14 * "Buy 10 AMZN"
  Assets:Trade:Cash                        -10000.00 USD
  Assets:Trade:Positions                       10 AMZN {1000.00 USD}

以 2000 USD 每股的价格出售 3 股 AMZN 记为:

; 单价
2018-08-30 * "Sell 3 AMZN"
  Assets:Trade:Positions                       -3 AMZN {1000.00 USD} @ 2000.00 USD
  Assets:Trade:Cash                          6000.00 USD
  Income:Trade:PnL                          -3000.00 USD

; 总价
2018-08-30 * "Sell 3 AMZN"
  Assets:Trade:Positions                       -3 AMZN {1000.00 USD} @@ 6000.00 USD
  Assets:Trade:Cash                          6000.00 USD
  Income:Trade:PnL                          -3000.00 USD

Beancount 提供了 5 种簿记方法:

  • STRICT:每次取豆子时,必须要明确匹配到某个 lot,不允许模糊匹配(例外是 {},代表全部取出)。这也是默认的模式。
  • FIFO:即先进先出(First-In, First-Out),每次取豆子时自动从最老的豆子开始取,直到取光。
  • LIFO:即后进先出(Last-In, First-Out),与 FIFO 相反,最新的豆子优先被取出。
  • AVERAGE:每次重新计算 cost basis。该方法暂未实现。
  • NONE:完全不合并,允许不同符号的 lot 同时存在。

Beancount 中可简写省略掉一些具体数值,因为 Beancount 会根据已有条件自动算出来。

周期性记账

现在越来越多的产品趋向于会员订阅制,以及可能存在的分期付款等,需要用到周期性记账的功能。

该功能在 Beancount 中需开启 beancount.plugins.forecast 插件,详见forecast doc

引入插件之后,咱们看看语法,很容易阅读和理解:

2021-05-17 # "House Rent [MONTHLY]"
  Expenses:House:Rent   3000 CNY
  Assets:DebitCard:CMB

2014-03-08 # "Electricity bill [MONTHLY UNTIL 2019-12-31]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

2014-03-08 # "Electricity bill [MONTHLY REPEAT 10 TIMES]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

2014-03-08 # "Electricity bill [WEEKLY SKIP 1 TIME REPEAT 10 TIMES]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

2014-03-08 # "Electricity bill [DAILY SKIP 3 TIMES REPEAT 1 TIME]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD

2022-02-08 # "Electricity bill [MONTHLY UNTIL 2022-12-31]"
  Expenses:Electricity                      50.10 USD
  Assets:Checking                          -50.10 USD
  • 周期有: YEARLY、MONTHLY、WEEKLY、DAILY
    • 后可接:UNTIL 2022-12-31
  • 循环有:REPEAT {integer} TIMES、REPEAT 1 TIME、SKIP {integer} TIME(S) REPEAT {integer} TIMES

GitHub Actions

施工中……

参考:使用 Beancount 管理家庭财务 · 构建我的被动收入 (bmpi.dev)

脚本导入

施工中……

参考:https://github.com/zsxsoft/my-beancount-scripts

参考文章

科普贴:最通俗地说清楚"复式记账法"

Beancount – 命令行复式簿记 | wzyboy’s blog

复式借贷记账法 Beancount (1) - 基础知识

Beancount - Visual Studio Marketplace

使用 Beancount 管理家庭财务

使用 Beancount 记账篇三:周期账单

标签 :
comments powered by Disqus

相关文章

如何在 React 中进行状态管理?使用 Zustand!

计数器 import { create } from 'zustand' const useStore = create(set => ({ count: 1, inc: () => set(state => ({ count: state.count + 1 })), })) function Controls() { const inc = useStore(state => state.inc) return <button onClick={inc}>one up</button> } function Counter() { const count = useStore(state => state.count) return <h1>{count}</h1> } 用法 创建状态 state 操作 action Basic typescript usage doesn’t require anything special except for writing create<State>()(...) instead of create(...)… import { create } from 'zustand' interface BearState { bears: number increase: (by: number) => void } const useBearStore = create<BearState>()((set)

阅读更多

Neo4j: 图数据基本概念、语法与增删改查

Neo4j 简介 Neo4j 使用属性图(Property Graph)模型1。 一个图包含节点(Objects)和边(Relationships)。 Neo4j 的属性图模型包含了: 节点 节点标签:用于区分节点的类型,0个或多个 边:源节点

阅读更多

BlockSuite 一款 Block Style 的编辑器,仅仅是编辑器!

先说结论 我的博客不太适合用 BlockSuite 作为编辑器,因为: 不知道如何渲染,我可以编辑,但是如何展示给匿名用户看? SEO 不太明确,也想着转成 HTML 来做,但是成本太高? 其他博主的调研:https://blog.nineya.

阅读更多