跳到主要内容

Beancount 中的精度与容差指南

管理数值精度是复式记账的基石。在数字簿记中,尤其是在处理多种货币、股票价格和零股时,微小的舍入误差会迅速导致令人沮丧的余额错误。Beancount 提供了一个复杂但直观的系统来处理精度和设置可接受的容差。本指南将引导您了解其工作原理。 ⚙️

核心精度概念

precision

Beancount 的主要目标是确保每笔交易的余额为零。但是,涉及价格或成本的计算通常会产生比实际记录更多小数位的结果。容差系统允许小的、可接受的不平衡。

自动容差推断

默认情况下,Beancount 自动推断每笔交易所需的容差。这种推断是针对每笔交易单独处理的,并且针对所涉及的每种货币单独计算。

规则很简单:容差是交易分录中出现的数字的最后一位有效数字的一半

例如,考虑以下购买:

2013-04-03 * "购买基金"
Assets:Fund 10.22626 FUND {37.61 USD}
Assets:Cash -384.61 USD

Beancount 推断的容差如下:

  • 对于 FUND 商品,数字 10.22626 有 5 位小数。容差是最后一位数字的一半,因此 0.00001div2=0.0000050.00001 \\div 2 = 0.000005 FUND
  • 对于 USD 商品,数字 -384.61 有 2 位小数。容差是最后一位数字的一半,因此 0.01div2=0.0050.01 \\div 2 = 0.005 USD

交易权重规则

在检查交易是否平衡时,Beancount 会计算每个分录的“权重”。此计算的规则是:

  1. 简单金额:如果分录只有金额(例如,Assets:Cash -100.00 USD),则其权重就是该确切金额。
  2. 价格分录:如果分录具有每单位价格(例如,10 FUND @ 38.46 USD),则其权重为 金额 × 价格
  3. 成本分录:如果分录具有总成本(例如,10 FUND {384.61 USD}),则其权重是总成本金额。
  4. 成本和价格:如果分录同时具有总成本和每单位价格(例如,10 FUND {384.61 USD} @ 38.46 USD),则仅使用总成本进行平衡。每单位价格被视为注释或备忘录。

精度推断规则

自动推断系统遵循一些特定规则:

  1. 数字格式
  • 整数金额(例如,10 USD不参与精度推断。
  • 可以自动推断的最大容差为 0.05 单位(例如,来自 10.1 USD 这样的数字)。如果您需要更大的容差,则必须手动指定。
  • 成本和价格(例如,{37.61 USD}不包括在容差推断中。仅使用分录的主要金额。
  • 如果同一货币的分录具有不同的精度(例如,-10.10 USD5.123 USD),则 Beancount 使用**最粗糙(最大)**的容差。在这种情况下,它将基于 -10.10 USD,从而产生 0.0050.005 USD 的容差。
  1. 默认处理 如果交易中没有带有小数位的数字可以从中推断出容差,则可以设置全局或特定于货币的默认容差。

    ; 为所有没有明确规则的货币设置默认容差
    option "inferred_tolerance_default" "*:0.001"

    ; 为 USD 设置特定的默认容差
    option "inferred_tolerance_default" "USD:0.003"
  2. 容差乘数 您可以全局增加所有推断的容差,通过一个固定的乘数。这对于放松对整个文件的检查非常有用,而无需更改每笔交易。1.2 的乘数将所有推断的容差增加 20%。

    option "inferred_tolerance_multiplier" "1.2"
  3. 基于成本的推断 虽然成本通常在容差推断中被忽略,但您可以指示 Beancount 使用它们。当最终金额(例如,现金提款)是交易中最精确的数字时,这很有用。

    option "infer_tolerance_from_cost" "TRUE"

余额断言

余额断言 (balance) 用于验证您的账户余额是否与特定日期的已知值匹配。它们也具有相关的容差。

基本格式

与交易类似,balance 断言的容差是从金额中的小数位数推断出来的。

; 断言余额为 4.271 RGAGX,容差为 ±0.0005
2015-05-08 balance Assets:Fund 4.271 RGAGX

; 断言余额为 4.27 RGAGX,容差为 ±0.005
2015-05-08 balance Assets:Fund 4.27 RGAGX

计算出的余额必须在此范围内。对于第二个示例,4.2654.2654.2754.275 之间的任何余额都将通过检查。

显式容差

如果推断的容差不合适,您可以使用波浪号 (~) 字符显式指定一个容差。

; 断言余额为 4.271 RGAGX,自定义容差为 ±0.01 RGAGX
2015-05-08 balance Assets:Fund 4.271 ~ 0.01 RGAGX

在这里,如果计算出的余额在 4.2614.2614.2814.281 RGAGX 之间,则断言将通过。

舍入管理

对于预计并接受计算中存在小残差的情况,Beancount 提供了系统地管理它们的工具。

舍入误差跟踪

您可以指定一个特殊帐户来自动收集舍入误差。这可以通过将微小的剩余金额扫入一个地方来使您的交易保持完美平衡。

首先,启用该选项并打开帐户:

option "account_rounding" "Equity:RoundingError"
2000-01-01 open Equity:RoundingError

现在,Beancount 将自动为任何未在其容差范围内平衡的交易添加第三条腿,并将差额过帐到 Equity:RoundingError

2013-02-23 * "购买"
Assets:Invest 1.245 RGAGX {43.23 USD}
Assets:Cash -53.82 USD

在此交易中,1.245times43.23=53.821351.245 \\times 43.23 = 53.82135。交易不平衡 0.00135-0.00135 USD。启用舍入选项后,Beancount 在内部将其视为:

2013-02-23 * "购买"
Assets:Invest 1.245 RGAGX {43.23 USD}
Assets:Cash -53.82 USD
Equity:RoundingError -0.00135 USD ; 自动添加

推断的数字精度

Beancount 还可以使用容差设置来自动舍入数字,甚至在将它们插入到账簿的数据结构中之前。

  1. 未指定容差:如果未定义容差,则数字以其完整精度使用。不会发生舍入。

  2. 具有默认容差:如果设置了默认容差,则数字将被量化到该级别。

    option "default_tolerance" "USD:0.001"

    使用此设置,像 53.82135 USD 这样的数字将被舍入并存储为 53.821 USD

  3. 具有舍入帐户:如果默认容差和舍入帐户都处于活动状态,则 Beancount 会量化数字并捕获残差。

    option "default_tolerance" "USD:0.01"
    option "account_rounding" "Equity:RoundingError"

    53.82135 USD 这样的数字将被存储为 53.82 USD,并且 -0.00135 USD 残差将被过帐到 Equity:RoundingError

实施细节

一些技术要点阐明了 Beancount 如何实现这种可靠性。

  1. 数字表示:Beancount 使用 Python 的 decimal 模块,而不是浮点数。这允许高达 28 位小数的精度,并避免了浮点数常见的二进制表示错误。

  2. DisplayContext 类:此类内部类处理所有用于显示目的的数字格式。它遵循特定于货币的精度设置,并且可以使用对齐的列和逗号来格式化输出。

  3. 精度与容差:区分这两个概念至关重要:

  • 精度 与数字的显示格式(显示多少位小数)有关。
  • 容差 是验证检查期间使用的不平衡允许量

最佳实践 ✨

以下是一些在您的账簿中管理精度的实用建议。

初始设置

对于大多数新账簿,这是一个稳健的起始配置:

; 对于大多数货币(例如,USD,EUR)来说,这是一个合理的默认值
option "inferred_tolerance_default" "*:0.005"

; 所有推断的容差的 10% 缓冲区
option "inferred_tolerance_multiplier" "1.1"

; 一个帐户来捕获所有舍入灰尘
option "account_rounding" "Equity:RoundingError"
2000-01-01 open Equity:RoundingError

故障排除提示

如果您遇到余额错误:

  • 向分录的金额添加小数位,以创建更严格、更准确的本地容差推断。
  • 对由于可预测的差异而失败的 balance 断言使用显式容差 (~)。
  • 专用帐户中跟踪舍入误差,以查看它们发生的位置和频率。
  • 如果您经常处理具有不同约定的货币(例如,日元没有小数),请考虑设置特定于货币的默认值

迁移策略

将这些概念应用于现有的、混乱的账簿时:

  1. 首先使用慷慨的全局容差(例如,*:0.05)和一个高乘数来使文件通过验证。
  2. 逐渐收紧容差并修复出现的错误。
  3. 在有问题的交易中添加显式数字到金额,让推断完成其工作。
  4. 监控舍入帐户的余额。较大或快速增长的余额可能表示需要调查的系统性问题。