复式记账法

核心点

  • 有借必有贷,借贷必相等
  • 借 = debit,贷 = credit

核心点不难理解,就是字面意思。只是注意不要陷入中文的语言陷阱中 —— 有人觉得"找银行借钱和找银行贷款"是同一个意思,于是就会搞混 debit 和 credit 的概念。

你只需要记住"贷 credit"才是需要偿还的即可(类比你的房贷)。如果你希望让"借"的理解更合理,你可以理解为是你把钱借给了银行,因为钱是你的,而你放在银行那存着。

记账过程

假设我使用某支付平台通过我的银行卡支付了 5 元购买了一瓶饮料,我的钱最终应该从我的账户流向商家的账户,理论上来说一个完备的记账流程大致如下:

事件 D 应收待清算 C 网关过渡户 C 商户待结算 D 应收应清算 D 备付金头寸 C 商户余额户
渠道扣款成功 D + 5 C + 5
支付成功 D - 5 C + 5
网联清分完成(完成明细对账) C - 5 D + 5
资金到账(完成资金对账) C - 5 D + 5
结算完成 D - 5 C + 5

我们先不管上述复杂的流程,按照下面的步骤从易到难逐步理解。


一、事件

事件的类型已经在上面标注出来了,你可以理解为这是行业内的通用实践,也可以理解为这是基于安全考量所必备的一些步骤。

比如,理论上来说,平台给商家打款,也可以不关心外部的网联的对账:

假设我是「XX 支付」,用户的钱在我的系统里本质上只是一个数字,在不涉及出入金的情况下,我完全可以随意修改这个数字。
平台的零钱 / 余额只能在平台内流通,直到你点击「提现到银行卡」,这笔钱我才真正需要和银行结算。
因此用户付费购买商品这个流程,我可以直接把 user.balance - 5, merchant.balance + 5,然后告诉商家「你的钱到账了」。
我们之所以需要消费上述的具体事件来推进流程,是因为这笔钱的来源并不是平台内的用户零钱,而是用户的银行卡,也就是说用户选择了「使用银行卡支付」。
所以如果我主动跳过其中的核心检查步骤,直接给商户加钱,那么万一用户的银行卡扣款失败,那我作为平台就损失了 5 元。


二、C / D 的特征

复习一下:C = credit 贷记,D = debit 借记。

我们不难发现上述表中有如下特征:

  • 每一列的 C 和 D 的和都是 0(除了最后两列)

    每一列代表的都是某个独立账户的变动,那么显然,除了最终的商户余额户和备付金头寸,其他账户的变动都是功能性的、短暂的。
    换句话来说,钱只是经过这些账户流入了商户的钱包,这些账户在不收取手续费的情况下,不应该从中拿取或者给予任何金额。
    为什么后两列不符合特征呢?因为商户通过卖饮料赚到了 5 元。

  • 每一行的 C 和 D 的和都是 0(除了第一行)

    每一行代表的都是一个事件产生的账户变动,这个变动显然是双向的,要符合借贷必相等的原则。
    为什么第一行不符合特征呢?因为商户本质是要收钱的,这个钱本身就是由外部用户的银行卡流入我们平台再结算给商户的。
    换句话来说,我向第一个杯子里倒了水,此时第一个杯子里的水就独立的增加了,后续向第二第三个杯子里转移水的时候,前一个杯子里的水会减少,后一个杯子里的水会增加。

  • 符号与账户类型对应

    账户本身的CD代表了账户的基本属性——表达资产还是负债
    那么对应的,如果我是一个负债账户C,此时我又额外背上了一笔贷款,显然我在这个账户上应该使用加法,而不是减法。
    同样,如果我还清了一笔债务D,那么我应该使用减法,而不是加法。


三、账户的功能

现在我们可以尝试理解,为什么我们会定义这么多账户了,每个账户又有什么功能。
主要考虑以下两个方面:

账户的属性 —— 资产还是负债?

这一步比较复杂,说起来也比较抽象,更像是一个逻辑题 / 文字游戏。我个人认为,单个账户本身的属性是什么都不重要,重要的是账户之间的关系。

比如,我用 0 / 1 来标注账户属性显然是完全可以的,关键点在于当前账户的对手账户必须是我的属性的对立面。

例如,商户的余额账户,显然和平台的备付金账户是对立的,因为商户的余额账户是平台的负债,而备付金账户是平台的资产。那么如果我用 1 标注了其中一者,那么另一个账户就必须用 0 标注。

然后我们在"符合逻辑"的基础上,可以根据实际情况来定义账户的属性。比如前面说了,商户的余额账户,显然是平台的负债,我们就给它赋予 C 的属性,然后给备付金账户赋予 D 的属性。


账户的功能 —— 用途是什么?

如果我们直接去探究某一个具体账户的功能,可能会感觉有些头疼。因此我们需要转换一下思路:
当我们站在技术的视角上审视这个问题,可能会更简单一些。作为平台,我们显然需要对安全性和健壮性负责,避免出现任何资金的误差导致资金的亏损和信誉的下降。

因此,我们可能会首先归纳出,外部事件的种类和功能,然后选取必要的事件进行消费,同时构建其状态机 —— 形成一个事件驱动的模型。
那么每个账户的功能就是在这个状态机中的一个节点,我们需要在这个节点上完成一些操作,然后将状态转移到下一个节点。

举例来说,上述记账过程中,最抽象的账户是「C 网关过渡户」,这看起来就很 Hack,不像是金融领域的术语和模型。但是如果我们站在状态机的视角来观察,你会发现它的功能其实是很明确的:帮助我们消费「支付成功」这个事件,也就是说,如果「C 网关过渡户」里面的负债过多,那显然意味着外部平台的资金没有及时完成支付,我们需要联系对应的平台进行处理。

类似的,如果我们的「D 应收应清算」里面的头寸过多,那么显然意味着外部平台比如银行没有及时的把资金转入我们的平台账户中,钱还卡在银行那,我们此时应该及时联系银行进行处理。

上述没有及时结算的状态,在金融领域往往被称为「风险敞口」
这个敞口需要我们及时的进行处理,处理的过程往往被称为「头寸管理(Position Management)」或者「平盘」
如果平台不及时的进行平盘,那么显然当用户大量提现的时候,平台就会出现资金链断裂的情况,导致资金的亏损和信誉的下降。


为了方便理解以上陈述,我们做出一个假设:银行渠道扣款和支付成功是一回事,于是可以剔除「C 网关过渡户」进行简化,表格也就变成了如下样貌:
事件 D 应收待清算 C 商户待结算 D 应收应清算 D 备付金头寸 C 商户余额户
渠道扣款成功 D + 5 C + 5
网联清分完成(完成明细对账) C - 5 D + 5
资金到账(完成资金对账) C - 5 D + 5
结算完成 D - 5 C + 5

明细对账?我们也不要了!钱收到了就行!

事件 D 应收待清算 C 商户待结算 D 备付金头寸 C 商户余额户
渠道扣款成功 D + 5 C + 5
资金到账(完成资金对账) C - 5 D + 5
结算完成 D - 5 C + 5

再简化就不礼貌了,也不现实了,但我们可以让它变得更抽象,更明显的表达状态机的思想:

Events D Account1 C Account2 D Position C Merchant
Init Event D + 5 C + 5
Processing Event C - 5 D + 5
Final Event D - 5 C + 5

复式记账法
https://kayce.world/chores/double_entry_bookkeeping/
Author
kayce
Posted on
December 16, 2024
Licensed under