본문으로 건너뛰기

재고 관리

Beancount에서 재고를 효과적으로 관리하는 방법을 알아보세요. 주식 및 통화와 같은 자산 추적, 취득원가 이해, 포트폴리오 성과 개선을 위한 자본 이득 계산에 중점을 둡니다.

Beancount재고 시스템은 시간이 지남에 따라 사고팔리는 주식, 펀드 또는 외화와 같은 자산을 추적하는 강력한 기능입니다. 이는 자본 이득 계산과 포트폴리오 성과 이해에 필수적인 취득원가를 정밀하게 추적할 수 있게 해줍니다. 이 튜토리얼은 원장에서 재고를 관리하는 핵심 메커니즘을 다룹니다.

핵심 개념

재고 관리의 핵심은 포지션을 추적하는 것입니다. "포지션"은 단순히 계정에 보유된 상품의 수량입니다. Beancount는 두 가지 기본 유형의 포지션을 구분합니다.

포지션 유형

  1. 단순 포지션(원가 없음): 이는 표준 잔액 게시입니다. 관련 취득원가 없이 상품의 수량을 나타냅니다. 현금이나 단순 잔액 확인에 적합합니다.

    Assets:Bank:Checking      100.00 USD
  2. 취득원가 포함 포지션: 이 유형의 포지션은 상품의 수량과 단위뿐만 아니라 취득 당시의 원가도 포함합니다. 이는 재고 추적의 기초입니다. 원가는 중괄호 {} 안에 지정됩니다.

    Assets:Invest:VTSAX      10 VTSAX {100.00 USD, "lot-1"}

    이 예시에서는 VTSAX 10단위를 보유하고 있습니다. 각 단위는 $100.00 USD의 원가로 취득되었습니다. 이 특정 주식 묶음은 "로트"로 식별됩니다.

재고 작업

재고에 대해 수행할 수 있는 두 가지 기본 작업이 있습니다:

  1. 증가(재고 추가): 상품을 매수하면 재고가 증가합니다. 특정 수량과 취득원가를 가진 새 로트를 생성합니다.

    2024-01-15 * "Buy shares"
      Assets:Invest:STOCK     50 STOCK {25.00 USD, "lot-1"}
      Assets:Bank:Checking   -1250.00 USD

    여기서는 STOCK 50단위를 단위당 $25.00 USD에 매수합니다. 이는 Assets:Invest:STOCK 계정에 로트를 생성합니다.

  2. 감소(재고 제거): 상품을 매도하면 재고가 감소합니다. 어느 로트에서 매도하는지 지정해야 합니다. 이는 중괄호 안에 일치하는 정보를 제공하여 수행됩니다.

    2024-01-20 * "Sell shares"
      Assets:Invest:STOCK    -25 STOCK {25.00 USD}
      Assets:Bank:Checking    625.00 USD

    거래에서는 단위당 $25.00 USD에 매수한 로트에서 STOCK 25단위를 매도합니다.

예약 방법

재고를 감소시킬 때 여러 로트가 감소 기준과 일치하는 경우 Beancount는 어떤 특정 로트를 사용할지 결정하는 규칙이 필요합니다. 이 규칙을 "예약 방법"이라고 합니다. 파일 전체에 대한 기본값을 옵션으로 설정하거나 open 지시문에서 특정 계정에 고유한 방법을 지정할 수 있습니다.

Beancount 3.2.3은 7가지 방법 이름을 허용합니다: STRICT(기본값), STRICT_WITH_SIZE, NONE, FIFO, LIFO, HIFOAVERAGE. 그중 6가지가 구현되어 있습니다. AVERAGE는 구문 분석은 되지만 아래 AVERAGE 섹션에서 설명하는 것처럼 감소를 예약해야 할 때 오류를 발생시킵니다.

1. STRICT(기본값)

STRICT 방법은 기본값이며 가장 안전한 예약 방법입니다. 명시적이고 모호하지 않은 일치를 강제합니다.

2024-01-01 open Assets:Invest:STOCK "STRICT"
  • 정확한 로트 일치 필요: 감소 게시의 원가 지정자({...})는 원가, 취득 날짜, 레이블 또는 이들의 조합으로 단일 로트를 식별해야 합니다.
  • 모호한 일치 시 오류: 지정자가 둘 이상의 로트와 일치하는 경우 Beancount는 추측하는 대신 AmbiguousMatchError를 발생시킵니다.
  • 예외: 감소가 지정자가 일치하는 총 단위 수를 정확히 제거하는 경우 빈 지정자({})가 허용되며 감소는 해당 로트들에 분할됩니다.

이 원장은 두 개의 로트를 보유하고 있으며 원가를 지정하여 그중 하나를 매도합니다. 이는 모호하지 않습니다:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Strict  STK  "STRICT"
1970-01-01 open Assets:Broker:Cash    USD
1970-01-01 open Income:Gains          USD
 
2024-01-10 * "Buy the first lot"
  Assets:Broker:Strict    10 STK {100.00 USD}
  Assets:Broker:Cash   -1000.00 USD
 
2024-02-10 * "Buy the second lot"
  Assets:Broker:Strict    10 STK {120.00 USD}
  Assets:Broker:Cash   -1200.00 USD
 
; The cost identifies exactly one lot, so STRICT is satisfied.
2024-06-01 * "Sell the $120.00 lot"
  Assets:Broker:Strict   -10 STK {120.00 USD} @ 150.00 USD
  Assets:Broker:Cash    1500.00 USD
  Income:Gains

오류 없이 로드되며 $300.00의 이득을 Income:Gains에 기록하고 계정에 10 STK {100.00 USD}를 남깁니다.

마지막 게시를 빈 지정자로 바꾸면 동일한 파일이 실패합니다:

; Rejected under STRICT: "-10 STK {}" matches both lots.
2024-06-01 * "Sell 10 shares"
  Assets:Broker:Strict  -10 STK {} @ 150.00 USD
  Assets:Broker:Cash   1500.00 USD
  Income:Gains

Beancount는 "-10 STK {}"에 대한 모호한 일치를 보고하고 후보를 나열합니다. 그러나 전체 포지션을 매도하는 것은 괜찮습니다. 선택할 항목이 남아 있지 않기 때문입니다:

; Allowed under STRICT: -20 STK is the entire holding, so the empty
; specifier is split across both lots.
2024-06-01 * "Close the position"
  Assets:Broker:Strict  -20 STK {} @ 150.00 USD
  Assets:Broker:Cash   3000.00 USD
  Income:Gains

이는 $800.00의 이득을 기록합니다 — $3,000.00의 매도 대금에서 $1,000.00 + $1,200.00의 원가를 뺀 금액 — 그리고 계정을 비웁니다. 이는 STRICT 자체의 속성이며 STRICT_WITH_SIZE로 전환해야 하는 것이 아닙니다.

2. FIFO(선입선출)

FIFO 방법은 자동으로 가장 오래된 사용 가능한 로트에 대해 감소를 예약합니다.

2024-01-01 open Assets:Invest:STOCK "FIFO"
  • 자동 해결: 가장 오래된 일치 로트를 선택하여 모호성을 해결합니다.
  • 시간순 일치: 가장 오래 보유한 자산을 매도한다고 가정합니다. 여러 세무 당국은 로트를 식별하지 않은 경우 이를 기본값으로 간주합니다.

3. LIFO(후입선출)

LIFO 방법은 FIFO의 반대입니다. 가장 최신 사용 가능한 로트에 대해 감소를 예약합니다.

2024-01-01 open Assets:Invest:STOCK "LIFO"
  • 역시간순: 가장 최근에 취득한 일치 로트를 선택합니다.
  • 가장 최신, 가장 비싼 것이 아님: LIFO는 취득 날짜로만 선택합니다. 가격이 상승하는 동안에는 우연히 가장 비싼 주식을 매도하게 되지만, 가장 최신 로트가 가장 저렴한 경우 — 아래 예시가 보여주려는 것 — LIFO는 가장 작은 이득이 아닌 가장 큰 이득을 실현합니다. 항상 가장 비싼 주식을 매도하는 방법은 다음에 설명하는 HIFO입니다.

4. HIFO(최고원가선출)

HIFO 방법은 날짜와 관계없이 가장 비싼 사용 가능한 로트에 대해 감소를 예약합니다.

2024-01-01 open Assets:Invest:STOCK "HIFO"
  • 원가 기준 일치: 취득원가가 가장 높은 일치 로트를 선택합니다.
  • 최소 실현 이득: 주어진 매도 가격에 대해 가장 비싼 주식을 매도하면 가장 작은 이득(또는 가장 큰 손실)이 실현됩니다. 사용 가능 여부는 관할권 문제입니다 — 예를 들어 미국에서는 로트 선택 자체가 매도 시점에 특정 식별을 요구합니다 — 따라서 이 방법을 회계 메커니즘으로 취급하고 세금 선택은 별도로 확인하십시오.

5. 동일한 로트에서 FIFO, LIFO 및 HIFO 비교

세 가지 방법은 가장 오래된 로트, 가장 최신 로트, 가장 비싼 로트가 서로 다른 세 로트인 경우에만 차이가 있습니다. 이 원장은 정확히 그렇게 구성합니다 — 로트 A가 가장 오래되었고, 로트 C가 가장 최신이며, 중간 로트 B가 가장 비쌉니다 — 그리고 예약 방법만 다른 세 계정에서 10주를 매도합니다:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Fifo  STK  "FIFO"
1970-01-01 open Assets:Broker:Lifo  STK  "LIFO"
1970-01-01 open Assets:Broker:Hifo  STK  "HIFO"
1970-01-01 open Assets:Broker:Cash  USD
1970-01-01 open Income:Gains        USD
 
; Lot A - the oldest, at $100.00 per share
2024-01-10 * "Buy lot A"
  Assets:Broker:Fifo     10 STK {100.00 USD}
  Assets:Broker:Lifo     10 STK {100.00 USD}
  Assets:Broker:Hifo     10 STK {100.00 USD}
  Assets:Broker:Cash  -3000.00 USD
 
; Lot B - the most expensive, at $120.00 per share
2024-02-10 * "Buy lot B"
  Assets:Broker:Fifo     10 STK {120.00 USD}
  Assets:Broker:Lifo     10 STK {120.00 USD}
  Assets:Broker:Hifo     10 STK {120.00 USD}
  Assets:Broker:Cash  -3600.00 USD
 
; Lot C - the newest, at $90.00 per share
2024-03-10 * "Buy lot C"
  Assets:Broker:Fifo     10 STK {90.00 USD}
  Assets:Broker:Lifo     10 STK {90.00 USD}
  Assets:Broker:Hifo     10 STK {90.00 USD}
  Assets:Broker:Cash  -2700.00 USD
 
; Sell 10 shares out of each account at $150.00 and let each
; account's booking method choose which lot leaves.
2024-06-01 * "Sell 10 shares from each account"
  Assets:Broker:Fifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Lifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Hifo    -10 STK {} @ 150.00 USD
  Assets:Broker:Cash   4500.00 USD
  Income:Gains

오류 없이 로드되며 총 $1,400.00의 이득을 다음과 같이 기록합니다:

계정방법예약된 로트취득원가실현 이득남은 로트
Assets:Broker:FifoFIFO로트 A, 2024-01-10$100.00$500.0010 @ $120.00, 10 @ $90.00
Assets:Broker:LifoLIFO로트 C, 2024-03-10$90.00$600.0010 @ $100.00, 10 @ $120.00
Assets:Broker:HifoHIFO로트 B, 2024-02-10$120.00$300.0010 @ $100.00, 10 @ $90.00

LIFO 행이 주목할 가치가 있습니다: 가장 최신 로트가 가장 저렴했기 때문에 세 가지 중 가장 큰 이득을 실현했습니다.

6. STRICT_WITH_SIZE

STRICT_WITH_SIZESTRICT에 추가 타이브레이커 하나가 더해진 것입니다: 여러 로트가 일치하지만 그중 정확히 하나가 제거하려는 단위 수를 정확히 보유하고 있는 경우 해당 로트가 선택됩니다.

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Sized  STK  "STRICT_WITH_SIZE"
1970-01-01 open Assets:Broker:Cash   USD
1970-01-01 open Income:Gains         USD
 
2024-01-10 * "Buy 10 shares"
  Assets:Broker:Sized    10 STK {100.00 USD}
  Assets:Broker:Cash  -1000.00 USD
 
2024-02-10 * "Buy 7 shares"
  Assets:Broker:Sized     7 STK {120.00 USD}
  Assets:Broker:Cash   -840.00 USD
 
; Only one lot holds exactly 7 units, so the empty specifier resolves.
2024-06-01 * "Sell 7 shares"
  Assets:Broker:Sized    -7 STK {} @ 150.00 USD
  Assets:Broker:Cash   1050.00 USD
  Income:Gains

이는 $120.00 로트에 대해 $210.00의 이득을 기록합니다. open 줄에 "STRICT"가 있는 동일한 파일은 "-7 STK {}"에 대한 모호한 일치로 실패합니다.

7. AVERAGE(허용되지만 구현되지 않음)

AVERAGE는 유효한 이름입니다 — option "booking_method" "AVERAGE"open … "AVERAGE" 모두 구문 분석됩니다 — 그러나 Beancount 3.2.3에는 그 뒤에 구현이 없습니다. 여기의 모든 것은 매도까지 로드됩니다:

1970-01-01 commodity STK
1970-01-01 open Assets:Broker:Avg   STK  "AVERAGE"
1970-01-01 open Assets:Broker:Cash  USD
1970-01-01 open Income:Gains        USD
 
2024-01-10 * "Buy 10 shares at $10.00"
  Assets:Broker:Avg      10 STK {10.00 USD}
  Assets:Broker:Cash  -100.00 USD
 
2024-02-10 * "Buy 10 more at $8.00"
  Assets:Broker:Avg      10 STK {8.00 USD}
  Assets:Broker:Cash   -80.00 USD
 
; An average-cost engine would book this at $9.00 per share. This one refuses.
2024-06-01 * "Sell 5 shares"
  Assets:Broker:Avg      -5 STK {}
  Assets:Broker:Cash    45.00 USD
  Income:Gains

해당 감소를 예약해야 하는 순간 로더는 다음으로 중지합니다:

AVERAGE method is not supported

이를 중심으로 원장을 계획하지 마십시오. 오늘 평균원가 동작이 필요하다면 NONE 계정에 포지션을 유지하고 평균을 직접 계산하거나 각 로트를 추적하고 로트 수준 이득을 수용하십시오.

8. NONE

NONE 방법은 로트 일치를 완전히 비활성화합니다.

2024-01-01 open Assets:Invest:STOCK "NONE"
  • 로트 일치 없음: Beancount는 감소를 증가와 일치시키려고 시도하지 않습니다.
  • 혼합 부호 허용: 이를 통해 계정은 동일한 상품의 양수 및 음수 잔액을 동시에 보유할 수 있습니다. 이 동작은 Ledger CLI 도구가 상품을 처리하는 방식과 유사합니다.

로트 지정

"로트"는 특정 시점과 가격에 취득한 상품의 특정 블록입니다. 포지션을 생성하거나 감소시킬 때 로트 속성을 자세히 지정할 수 있습니다.

전체 지정

재고를 증가(매수)할 때 로트에 대해 최대 세 가지 속성을 단일 중괄호 쌍 안에 쉼표로 구분하여 지정할 수 있습니다:

Assets:Invest:STOCK  10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}
  • 100.00 USD단위당으로 표시된 취득원가.
  • 2024-01-15 — 취득 날짜. 생략하면 Beancount는 거래 날짜에서 이를 자동으로 채웁니다. 이것이 위의 오류 메시지가 모든 로트에 날짜를 표시하는 이유입니다.
  • "로트-식별자" — 선택적 문자열 레이블.

세 가지 모두 선택 사항이지만 최소한 취득원가를 제공하는 것이 표준 관행입니다. 중괄호는 한 줄에 있어야 하며 원장의 주석은 ;로 시작하고 절대 #로 시작하지 않습니다.

일치 방법

재고를 감소(매도)할 때 동일한 구문을 사용하여 매도할 로트를 지정합니다.

  • 원가로 일치: 가장 일반적인 방법입니다.

    Assets:Invest:STOCK  -5 STOCK {100.00 USD}
  • 날짜로 일치: 원가가 동일한 경우 취득 날짜를 사용하여 구분할 수 있습니다.

    Assets:Invest:STOCK  -5 STOCK {2024-01-15}
  • 레이블로 일치: 레이블은 로트를 식별하는 확실한 방법을 제공합니다.

    Assets:Invest:STOCK  -5 STOCK {"lot-identifier"}
  • 로트를 예약 방법에 맡기기: 빈 중괄호 집합 {}은 로트를 지정하지 않으므로 계정의 예약 방법이 선택합니다. FIFO, LIFO 또는 HIFO에서는 가장 오래된, 가장 최신 또는 가장 비싼 일치 로트입니다. 기본 STRICT에서는 감소가 일치 로트를 정확히 비우지 않는 한 AmbiguousMatchError입니다.

    Assets:Invest:STOCK  -5 STOCK {}

가격 처리

취득원가({})와 가격(@)의 차이를 이해하는 것이 중요합니다. 이들은 서로 다른 목적을 가지며 상호 교환할 수 없습니다.

가격과 원가

  • {cost}: 자산의 취득원가를 정의합니다. 이는 재고 로트 자체의 일부이며 감소 예약 및 자본 이득 계산에 사용됩니다.
  • @ price: 거래 시점의 시장 가격을 기록하는 주석입니다. 통화 변환 또는 특정 날짜의 시장 가치 기록에 사용됩니다.

세 가지 시나리오는 다음과 같습니다:

  1. 가격 주석(변환): 한 통화에서 다른 통화로 변환하려면 @를 사용합니다.

    Assets:Forex     1000 USD @ 0.85 EUR
  2. 취득원가(취득): 자산 매수 시 원가를 설정하려면 {}를 사용합니다.

    Assets:Invest    10 STOCK {100.00 USD}
  3. 둘 다(가격 기록이 있는 매도): 자산 매도 시 {}로 매도할 로트를 식별하고 @로 매도 가격을 기록합니다. 이는 자동 자본 이득 계산을 가능하게 합니다.

    Assets:Invest    -10 STOCK {100.00 USD} @ 105.00 USD

    이 항목은 각각 $100.00에 매수한 로트에서 STOCK 10주를 주당 $105.00에 매도합니다.

가격 사용 규칙

  1. 가격 주석(@)은 어떤 로트가 예약되는지에 영향을 미치지 않습니다. 로트 일치는 전적으로 취득원가({})와 계정의 예약 방법에 의해 처리됩니다.
  2. @ 기호는 다음에만 사용됩니다:
  • 통화 변환.
  • 거래 시점의 자산 시장 가치 기록.
  • 자본 이득 계산을 위한 매도 가격 제공.

구성

예약 방법을 전역적으로 또는 계정별로 구성할 수 있습니다.

전역 예약 방법

option 지시문을 사용하여 전체 Beancount 파일에 대한 기본 예약 방법을 설정할 수 있습니다.

option "booking_method" "STRICT"

허용되는 값은 "STRICT"(아무것도 설정하지 않을 때의 기본값), "STRICT_WITH_SIZE", "NONE", "FIFO", "LIFO", "HIFO""AVERAGE"입니다. 다른 문자열은 로드 시 옵션 'booking_method'에 대한 오류로 거부됩니다. "AVERAGE"는 여기와 open에서 허용되지만 그 아래에서 감소를 예약하면 위의 AVERAGE 섹션에서 설명한 것처럼 실패합니다.

계정별 재정의

계정마다 다른 방법을 사용하는 것이 유용한 경우가 많습니다. 예를 들어 퇴직 계좌에는 FIFO를 사용하고 과세 중개 계좌에는 특정 세금 로트를 판매하는지 확인하기 위해 STRICT를 사용할 수 있습니다. 계정을 열 때 예약 방법을 설정할 수 있습니다.

2024-01-01 open Assets:Retirement:401K "FIFO"
2024-01-01 open Assets:Taxable:Stock  "STRICT"

모범 사례

  1. 재고 구성: 원장을 깔끔하고 단순하게 유지하려면 보유한 각 고유 상품에 대해 별도의 계정을 사용하고 open 지시문에서 해당 상품으로 제한하는 것이 좋습니다.

    ; GOOD: separate accounts by commodity, each constrained to one
    2024-01-01 open Assets:Invest:VTSAX  VTSAX
    2024-01-01 open Assets:Invest:VFIAX  VFIAX

    재고 관리가 복잡해지므로 동일한 계정에 다른 주식이나 펀드를 혼합하지 마십시오. open의 상품 목록은 두 재고를 조용히 혼합하는 대신 잘못된 게시를 Beancount가 거부하게 만듭니다.

  2. 로트 관리:

  • 세금 손실 수확이나 직원 주식 부여와 같은 특정 거래에 대해 의미 있는 레이블을 사용하십시오.

    Assets:Invest:STOCK  10 STOCK {100.00 USD, "tax-loss-harvest-2024"}
  • 거래를 주석으로 문서화하십시오. 이렇게 하면 원장을 나중에 더 쉽게 읽고 이해할 수 있습니다.

    Assets:Invest:STOCK  -10 STOCK {100.00 USD} @ 110.00 USD ; Gain: 10%
  1. 디버깅: 오류나 예기치 않은 동작이 발생하면 Beancount는 재고 상태를 검사하는 도구를 제공합니다.
  • 재고 상태 검사: bean-doctor 도구는 파일의 어느 시점에서든 모든 재고의 정확한 상태를 보여줄 수 있습니다.

    <LINENO>를 거래 직후의 줄 번호로 바꾸면 그 효과를 볼 수 있습니다.

  • 로트 일치 확인: bea check 도구는 전체 파일을 검증합니다. STRICT 모드의 모호한 로트 일치와 같은 모든 예약 오류를 잡아냅니다.

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