用 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) => ({ bears: 0, increase: (by) => set((state) => ({ bears: state.bears + by })), })) const useFishStore = create((set) => ({ salmon: 1, tuna: 2, deleteEverything: () => set({}, true), // clears the entire store, actions included deleteTuna: () => set((state) => omit(state, ['tuna']), true), })) const useSoundStore = create((set, get) => ({ sound: "grunt", action: () => { const sound = get().sound // you still have access to state outside of it through get // ... } }) export default useBearStore 在 react 之外使用呢?

阅读更多
beancount-gs 一款 self-hosted 复式记账程序,简化你的记账方式!

本程序将部署在 macOS 上,不使用 Docker(mac 上的 Docker 太卡了) 动机 我利用 beancount 来记账已经有一段时间了,但有些痛点问题困扰着我: 没有 web 界面,fava 真的只能用来展示和分析且不好理解,需要有一个方便记账的界面 text 记账的方式,在 vscode 插件能力有限的情况下,很容易忘记 assets 和 expenses 到底叫啥名,如果有 web 界面那么一定会好很多,一个下拉列表就可以解决 有没有解决方案,不需要自己造轮子的那种?

阅读更多
JWT 的应用场景思考

JWT 的应用场景思考 简述 JWT JWT,全称 JSON Web Token。是一种开放标准,用于在各方之间安全传递信息,它是以 Base64 编码 json 对象的 token 。基于 token 的权限验证,与传统的 Session 认证完全不同,它不需要服务端保持 Session 记录,连用户状态都不需要关心。一旦用户登录网站,服务器就会生成 token,之后客户端每次登录时在HTTP的头信息中带上 token 即可。

阅读更多