본문으로 건너뛰기

정밀도 및 허용 오차

Beancount의 정밀도 및 허용 오차 시스템이 복수의 통화와 분수 주식을 포함한 복잡한 거래를 다룰 때 복식부기 원장의 균형을 유지하는 데 어떻게 도움이 되는지 알아보세요.

수치 정밀도 관리는 복식부기의 핵심입니다. 디지털 부기, 특히 여러 통화, 주식 가격, 분수 주식을 다룰 때 작은 반올림 불일치가 곧 좌절스러운 균형 오류로 이어질 수 있습니다. Beancount는 정밀도 처리 및 허용 가능한 허용 오차 설정을 위한 정교하면서도 직관적인 시스템을 제공합니다. 이 가이드는 그 작동 방식을 안내합니다. ⚙️

이 페이지의 모든 숫자는 Beancount 3.2.3에서 확인되었으며, 경계값도 포함합니다: 각 예제는 어떤 잔차가 허용되고 어떤 것이 한 자리 더 많은지 명시합니다.

핵심 정밀도 개념

Beancount의 주요 목표는 모든 거래가 0으로 균형을 맞추도록 하는 것입니다. 그러나 가격 또는 원가 계산은 종종 기록하기에 실용적인 것보다 더 많은 소수점 자리를 가진 결과를 생성합니다. 허용 오차 시스템은 작고 허용 가능한 불균형을 허용합니다.

자동 허용 오차 추론

기본적으로 Beancount는 각 거래에 필요한 허용 오차를 자동으로 추론합니다. 이 추론은 각 거래마다 개별적으로 처리되며 관련된 각 통화에 대해 별도로 계산됩니다.

규칙은 하나의 곱셈입니다: 통화의 허용 오차는 해당 통화의 포스팅 금액에서 보이는 가장 작은 자릿수에 tolerance_multiplier 옵션(기본값 0.5)을 곱한 값입니다. 이 기본값에서 허용 오차는 마지막 유효 숫자의 절반입니다.

예를 들어, 다음 구매를 고려해 보세요:

2013-04-03 * "Buy Fund"
  Assets:Fund     10.22626 FUND {37.61 USD}
  Assets:Cash     -384.61 USD

Beancount는 다음과 같이 허용 오차를 추론합니다:

  • FUND 상품의 경우, 숫자 10.22626은 소수점 5자리를 가집니다. 허용 오차는 마지막 숫자의 절반이므로 $0.00001 \div 2 = 0.000005$ FUND입니다.
  • USD 통화의 경우, 숫자 -384.61은 소수점 2자리를 가집니다. 허용 오차는 마지막 숫자의 절반이므로 $0.01 \div 2 = 0.005$ USD입니다.

현금 항목이 허용 오차 측정 기준입니다: 10.22626 × 37.61384.6096386이므로 이 거래는 0.0003614 USD가 0보다 부족하며 로드됩니다. 현금 항목을 -384.60으로 반올림하면 차이는 0.0096386 USD가 되어 0.005 허용 오차를 초과하고, Beancount는 Transaction does not balance를 보고합니다.

거래 가중치 규칙

거래 균형을 확인할 때 Beancount는 각 포스팅의 "가중치"를 계산합니다. 이 계산 규칙은 다음과 같습니다:

  1. 단순 금액: 포스팅에 금액만 있는 경우(예: Assets:Cash -100.00 USD), 가중치는 해당 정확한 금액입니다.
  2. 가격 포스팅: 포스팅에 단위당 가격이 있는 경우(예: 10 FUND @ 38.46 USD), 가중치는 amount × price입니다.
  3. 단위당 원가: 중괄호 하나는 한 단위의 원가를 나타내므로 10 FUND {384.61 USD}10 × 384.61 = 3,846.10 USD가 아닌 384.61 USD로 가중치가 계산됩니다.
  4. 총 원가: 이중 중괄호는 전체 포스팅의 원가를 나타내므로 10 FUND {{384.61 USD}}384.61 USD로 가중치가 계산됩니다. Beancount는 보관할 때 이를 단위당 원가 38.461 USD로 변환합니다.
  5. 원가와 가격: 포스팅에 원가와 단위당 가격이 모두 있는 경우(예: 10 FUND {384.61 USD} @ 400.00 USD), 균형 계산에는 원가만 사용됩니다. 가격은 산술이 아닌 보고를 위해 기록됩니다.

규칙 3과 4는 사람들이 많은 시간을 소비하게 만드는 부분이므로, 로드되는 파일에서 나란히 살펴보겠습니다:

1970-01-01 open Assets:Fund
1970-01-01 open Assets:Cash
 
; Per-unit cost: ten units at 384.61 each, so 3,846.10 USD leaves the
; cash account.
2013-04-03 * "Broker" "Buy at a per-unit cost"
  Assets:Fund     10 FUND {384.61 USD}
  Assets:Cash  -3846.10 USD
 
; Total cost: the braces double and 384.61 USD is the entire purchase.
; The lot is stored at 38.461 USD per unit.
2013-04-04 * "Broker" "Buy at a total cost"
  Assets:Fund      10 FUND {{384.61 USD}}
  Assets:Cash   -384.61 USD

계정은 두 개의 로트에 20 FUND로 끝나며, 그 사이에 4,230.71 USD원가 기준이 있습니다.

정밀도 추론 규칙

자동 추론 시스템은 몇 가지 특정 규칙을 따릅니다:

  1. 숫자 형식
  • 정수 금액(예: 10 USD)은 정밀도 추론에 기여하지 않습니다.
  • 소수점 한 자리가 금액이 의미할 수 있는 가장 거친 단위입니다: 0.1 × 0.5 = 0.05 단위. 그 이상이 필요하면 아래의 tolerance_multiplier 또는 통화별 기본값을 사용하세요.
  • 원가와 가격(예: {37.61 USD})은 기본적으로 허용 오차 추론에서 제외됩니다. 포스팅의 기본 금액만 사용됩니다.
  • 동일한 통화에 대한 포스팅의 정밀도가 다른 경우(예: -10.10 USD5.123 USD), Beancount는 가장 거친(가장 큰) 허용 오차를 사용합니다. 이 경우 -10.10 USD를 기준으로 $0.005$ USD의 허용 오차가 생성됩니다.
  1. 기본 처리 추론할 소수점 자리가 없는 거래의 경우 전역 또는 통화별 기본 허용 오차를 설정할 수 있습니다.

    ; Sets a default tolerance for all currencies without explicit rules
    option "inferred_tolerance_default" "*:0.001"
     
    ; Sets a specific default tolerance for USD
    option "inferred_tolerance_default" "USD:0.003"
  2. 허용 오차 배수 tolerance_multiplier 옵션은 허용 가능한 것으로 간주되는 가장 작은 자릿수의 비율입니다 — 추가되는 백분율이 아닙니다. 기본값은 0.5이므로 1.2로 설정해도 검사가 20% 느슨해지지 않습니다: 모든 추론 허용 오차가 기본값의 2.4배가 됩니다.

    option "tolerance_multiplier" "1.2"
     
    1970-01-01 open Assets:Cash
    1970-01-01 open Expenses:Fees
     
    ; The coarsest amount has two decimals, so the tolerance is
    ; 1.2 x 0.01 = 0.012 USD, and this residual of exactly 0.012 passes.
    ; At the default 0.5 the tolerance would be 0.005 and this would fail.
    2024-05-01 * "Bank" "Wire fee"
      Expenses:Fees      100.00 USD
      Assets:Cash       -99.988 USD

    이전 이름 inferred_tolerance_multiplier는 동일한 값을 설정하지만 로드 오류로 Renamed to 'tolerance_multiplier'.를 보고합니다.

  3. 원가 기반 추론 일반적으로 원가는 허용 오차 추론에서 무시되지만, Beancount에 이를 사용하도록 지시할 수 있습니다. 이는 최종 금액(예: 현금 인출)이 거래에서 가장 정밀한 숫자인 경우 유용합니다.

    option "infer_tolerance_from_cost" "TRUE"

다음은 옵션이 전혀 없는 기본 상태의 정확한 경계값입니다:

1970-01-01 open Assets:Cash
1970-01-01 open Expenses:Fees
 
; Two decimals on the coarsest amount, so the tolerance is
; 0.5 x 0.01 = 0.005 USD. This residual is exactly 0.005 and passes;
; -99.994 would be 0.006 and would fail.
2024-05-01 * "Bank" "Wire fee"
  Expenses:Fees      100.00 USD
  Assets:Cash       -99.995 USD

잔액 검증

잔액 검증(balance)은 특정 날짜에 계정의 잔액이 알려진 값과 일치하는지 확인하는 데 사용됩니다. 여기에도 관련 허용 오차가 있습니다.

기본 형식

balance 검증의 허용 오차는 금액의 소수점 자리 수에서 추론되지만 거래 내부에서 사용되는 것보다 두 배 넉넉합니다: tolerance_multiplier × 2 × the smallest digit. 기본 배수에서 이는 작성한 마지막 소수점 자리의 정확히 한 단위입니다.

; Asserts the balance is 4.271 RGAGX with a tolerance of +/-0.001
2015-05-08 balance Assets:Fund  4.271 RGAGX
 
; Asserts the balance is 4.27 RGAGX with a tolerance of +/-0.01
2015-05-08 balance Assets:Fund  4.27 RGAGX

비교는 포괄적입니다: 허용 오차와 정확히 같은 차이도 통과합니다. 두 번째 예의 경우 $4.26$에서 $4.28$ 사이의 모든 잔액이 검사를 통과하며, 4.2801Balance failed for 'Assets:Fund': expected 4.27 RGAGX != accumulated 4.2801 RGAGX (0.0101 too much)로 실패합니다.

1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
 
1970-01-02 * "Broker" "Opening position"
  Assets:Fund                4.28 RGAGX
  Equity:Opening-Balances   -4.28 RGAGX
 
; 4.28 is 0.01 away from the asserted 4.27, which is the whole tolerance.
2015-05-08 balance Assets:Fund   4.27 RGAGX

명시적 허용 오차

추론된 허용 오차가 적절하지 않은 경우 물결표(~) 문자를 사용하여 명시적으로 지정할 수 있습니다. 이것은 Beancount가 가진 유일한 명시적 허용 오차 구문이며 balance 지시문에서만 작동합니다 — 거래 포스팅 내부의 물결표는 구문 오류입니다.

1970-01-01 open Assets:Fund
1970-01-01 open Equity:Opening-Balances
 
1970-01-02 * "Broker" "Opening position"
  Assets:Fund                4.281 RGAGX
  Equity:Opening-Balances   -4.281 RGAGX
 
; Asserts the balance is 4.271 RGAGX with a custom tolerance of
; +/-0.01 RGAGX, so anything from 4.261 to 4.281 passes.
2015-05-08 balance Assets:Fund   4.271 ~ 0.01 RGAGX

보유량을 4.2811로 올리면 동일한 검증이 0.0101만큼 실패합니다.

반올림 관리

원가 및 가격 산술의 작은 잔차는 정상입니다. Beancount가 이를 처리하는 방식은 보이는 것보다 더 제한적입니다.

반올림 오류 추적

account_rounding 옵션은 잔차를 흡수하기 위한 계정을 지정합니다. 전체 계정 이름을 사용하며 작성한 그대로 저장됩니다 — equity 계정 옵션과 달리 equity 접두사가 추가되지 않습니다.

option "account_rounding" "Equity:Rounding"
 
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
; 1.245 x 43.23 = 53.82135, so this is 0.00135 USD short of balancing.
2013-02-23 * "Broker" "Purchase"
  Assets:Invest     1.245 RGAGX {43.23 USD}
  Assets:Cash      -53.82 USD

이 거래에서 1.245×43.23=53.821351.245 \times 43.23 = 53.82135입니다. 거래는 $-0.00135$ USD의 불균형이 있으며, 이는 추론된 0.005 USD 허용 오차 내에 있으므로 로드됩니다.

Beancount 3.2.3에서는 Equity:Rounding에 아무것도 전기되지 않습니다. 옵션은 구문 분석되어 저장되지만 로더의 어떤 단계에서도 잔차 포스팅을 삽입하지 않으므로 계정은 0으로 끝나고 허용 오차 밖에 있는 잔차는 여전히 정리되지 않고 오류로 처리됩니다:

option "account_rounding" "Equity:Rounding"
 
1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
; 0.10135 USD out, far past the 0.005 tolerance. Setting
; account_rounding does not rescue it:
;   Transaction does not balance: (0.10135 USD)
2013-02-23 * "Broker" "Purchase"
  Assets:Invest     1.245 RGAGX {43.23 USD}
  Assets:Cash      -53.72 USD

따라서 이 버전에서는 account_rounding을 비활성 상태로 간주하세요. 잔차를 허용하는 대신 기록하려면 세 번째 포스팅을 직접 작성하세요:

1970-01-01 open Assets:Invest
1970-01-01 open Assets:Cash
1970-01-01 open Equity:Rounding
 
2013-02-23 * "Broker" "Purchase"
  Assets:Invest      1.245 RGAGX {43.23 USD}
  Assets:Cash       -53.82 USD
  Equity:Rounding   -0.00135 USD

이 버전은 정확히 0으로 균형을 맞추며, 먼지는 보고할 수 있는 계정에 표시됩니다.

추론된 숫자 정밀도

Beancount는 작성한 숫자를 반올림하지 않습니다. default_tolerance 옵션은 존재하지 않으며 존재하지 않는 옵션은 Invalid option: 'default_tolerance'로 로드에 실패하고, 저장된 금액을 양자화하는 설정도 없습니다.

  1. 저장은 항상 정확합니다. 53.82135 USD를 작성하면 원장은 허용 오차 설정과 관계없이 53.82135 USD를 보유합니다. 허용 오차는 거래가 수락되는지 여부를 결정할 뿐 숫자를 편집하지 않습니다.

  2. 표시는 별도의 설정입니다. display_precision은 통화가 렌더링되는 소수 자릿수를 고정하며 저장된 값이나 잔액 검사에는 아무것도 변경하지 않습니다.

    option "display_precision" "USD:0.01"
     
    1970-01-01 open Assets:Cash
    1970-01-01 open Income:Interest
     
    ; Rendered as 53.82 USD, stored as 53.82135 USD.
    2024-06-30 * "Bank" "Interest"
      Assets:Cash          53.82135 USD
      Income:Interest     -53.82135 USD
  3. 반올림은 직접 작성해야 할 포스팅입니다. 산술에서 잔차를 제거하려면 위의 세 포스팅 예제처럼 소스에서 금액을 반올림하고 차이를 명시적으로 기록하세요.

구현 세부 사항

Beancount가 이 신뢰성을 달성하는 방법을 명확히 하는 몇 가지 기술적 요점이 있습니다.

  1. 숫자 표현: Beancount는 Python의 decimal 모듈을 사용하며 부동 소수점 숫자가 아닙니다. 기본 컨텍스트는 28개의 유효 숫자를 보유합니다 — 소수점 이후 자릿수가 아닌 총 자릿수 — 이는 부동 소수점에서 흔한 이진 표현 오류를 방지합니다.

  2. DisplayContext 클래스: 이 내부 클래스는 표시 목적으로 모든 숫자 형식을 처리합니다. display_precision이 고정하지 않는 한 파일의 숫자에서 각 통화의 정밀도를 추론하며, 정렬된 열과 쉼표로 출력을 형식화할 수 있습니다.

  3. 정밀도와 허용 오차: 이 두 개념을 구분하는 것이 중요합니다:

  • 정밀도는 숫자의 표시 형식 과 관련이 있습니다(표시되는 소수 자릿수).
  • 허용 오차는 검증 중에 사용되는 불균형 허용 한도 입니다.

모범 사례 ✨

원장에서 정밀도를 관리하기 위한 몇 가지 실용적인 권장 사항입니다.

초기 설정

대부분의 새 원장에서 이것은 견고한 시작 구성입니다:

; A floor for currencies that have no decimals to infer from
option "inferred_tolerance_default" "*:0.005"
 
; Leave the multiplier at its 0.5 default unless a real institution
; forces your hand; 1.2 would mean 2.4x the usual tolerance.
option "tolerance_multiplier" "0.5"

문제 해결 팁

균형 오류가 발생하는 경우:

  • 포스팅 금액에 소수 자릿수를 추가하여 더 정밀하고 정확한 로컬 허용 오차 추론을 만드세요.
  • 예측 가능한 불일치로 실패하는 balance 검증에는 명시적 허용 오차(~)를 사용하세요.
  • 실제 세 번째 포스팅으로 잔차를 전용 계정에 기록하여 발생 빈도를 보고할 수 있게 하세요.
  • 다른 관례를 가진 통화(예: JPY는 소수점 없음)를 자주 다루는 경우 통화별 기본값 설정을 고려하세요.

마이그레이션 전략

이 개념을 기존의 지저분한 원장에 적용할 때:

  1. 넉넉한 전역 허용 오차(예: *:0.05)와 더 높은 tolerance_multiplier로 시작하여 파일이 검증되도록 하세요.
  2. 점진적으로 허용 오차를 좁히고 나타나는 오류를 수정하세요.
  3. 문제가 있는 거래의 금액에 명시적 자릿수를 추가하여 추론이 제 역할을 하게 하세요.
  4. 반올림 계정의 잔액을 모니터링하세요. 크거나 빠르게 증가하는 잔액은 조사가 필요한 시스템적 문제를 나타낼 수 있습니다.

출처: https://beancount.io/ko/docs/Basics/precision