メインコンテンツへスキップ

Beancountのロット、取得原価、および記帳方法

株式や通貨を売却する際にBeancountがロットをどのように記帳するかを解説します。取得原価、ロット指定、STRICT、FIFO、LIFO、HIFOによる記帳、価格と原価の違い、口座ごとの上書き設定を取り上げます。

Beancount の在庫システムは、株式、投資信託、外貨など、時間の経過とともに売買される資産を追跡するための強力な機能です。取得原価を正確に追跡できるため、キャピタルゲインの計算やポートフォリオのパフォーマンス把握に欠かせません。このチュートリアルでは、台帳における在庫管理の核心的な仕組みを解説します。

コアコンセプト​

在庫管理の中心は、ポジションの追跡にあります。「ポジション」とは、単に口座に保有されているある商品の数量のことです。Beancount は、ポジションを大きく2種類に区別します。

ポジションの種類​

  1. 単純ポジション(原価なし): これは標準的な残高記帳です。取得原価を伴わない、ある商品の数量を表します。現金や単純な残高検証に適しています。

    Assets:Bank:Checking      100.00 USD
  2. 取得原価付きポジション: このタイプのポジションには、単位数と商品だけでなく、取得した際の原価も含まれます。これが在庫追跡の基盤です。原価は波括弧 {} の中に指定します。

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

    この例では、VTSAX を10単位保有しています。各単位は $100.00 USD で取得されました。この特定の株式のまとまりは「ロット」と呼ばれます。

在庫操作​

在庫に対して実行できる主な操作は2つあります。

  1. 増加(在庫への追加): 商品を購入すると、在庫が増加します。特定の単位数と取得原価を持つ新しいロットを作成します。

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

    ここでは、STOCK を1単位あたり $25.00 USD で50単位購入しています。これにより 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単位売却しています。

仕訳方法​

在庫を減少させるとき、複数のロットが一致する場合に、どのロットから取り出すかを決めるルールが必要です。このルールは「ブッキングメソッド」と呼ばれます。ファイル全体のデフォルトをオプションで設定するか、open ディレクティブで口座ごとに独自のメソッドを指定できます。

Beancount 3.2.3 は7つのメソッド名を受け付けます:STRICT(デフォルト)、STRICT_WITH_SIZE、NONE、FIFO、LIFO、HIFO、AVERAGE です。そのうち6つが実装されています。AVERAGE はパースされますが、減少をブッキングする必要が生じた瞬間にエラーを発生させます。以下に示す AVERAGE セクションを参照してください。

1. STRICT (既定値)​

STRICT メソッドはデフォルトであり、最も安全なブッキングメソッドです。明示的で曖昧さのないマッチングを強制します。

2024-01-01 open Assets:Invest:STOCK "STRICT"
  • 完全一致するロットを要求: 減少ポスティングの原価指定子({...})は、原価、取得日、ラベル、またはそれらの任意の組み合わせによって、単一のロットを特定する必要があります。
  • 曖昧な一致でエラー: 指定子が複数のロットに一致する場合、Beancount は推測せずに AmbiguousMatchError を発生させます。
  • 例外: 減少が指定子に一致する単位数の合計をちょうど取り除く場合、空の指定子({})が許可され、減少はそれらのロットに分割されます。

この台帳は2つのロットを保有し、そのうちの1つを原価を指定して売却します。これは曖昧さがありません。

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 は Ambiguous matches for "-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の比較​

3つのメソッドは、最も古いロット、最も新しいロット、最も高価なロットが3つの異なるロットである場合にのみ異なります。この台帳はまさにそのように構成されています — ロット A が最も古く、ロット C が最も新しく、中間のロット B が最も高価です — そしてブッキングメソッドだけが異なる3つの口座から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 の行は注目に値します。3つの中で最大の利益を実現しました。なぜなら、最も新しいロットが最も安価でもあったからです。

6. STRICT_WITH_SIZE​

STRICT_WITH_SIZE は STRICT に1つの追加のタイブレーカーを加えたものです。複数のロットが一致するが、そのうちのちょうど1つが、削除する単位数を正確に保持している場合、そのロットが選択されます。

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" を指定した同一のファイルは Ambiguous matches for "-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 ツールが商品を扱う方法に似ています。

ロット仕様​

「ロット」とは、特定の時点と価格で取得された商品の特定のまとまりです。ポジションを作成または減少させるとき、そのロットの属性を詳細に指定できます。

完全仕様​

在庫を増加(購入)するとき、ロットに対して最大3つの属性を、1対の波括弧の中にカンマ区切りで指定できます。

Assets:Invest:STOCK  10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}
  • 100.00 USD — 取得原価。1単位あたりで表現します。
  • 2024-01-15 — 取得日。省略すると Beancount は取引日からこれを補います。これが、上記のエラーメッセージがすべてのロットに日付を表示する理由です。
  • "lot-identifier" — オプションの文字列ラベル。

3つすべてがオプションですが、少なくとも取得原価を提供することが標準的な慣行です。波括弧は1行に保つ必要があり、台帳内のコメントは ; で始まり、# は決して使用しません。

マッチング方法​

在庫を減少(売却)するとき、同じ構文を使ってどのロットから売却するかを指定します。

  • 原価でマッチング: これが最も一般的な方法です。

    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: 取引時点の市場価格を記録する注釈です。通貨換算や特定の日付の市場価値を記すために使用されます。

3つのシナリオを以下に示します。

  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 の売却価格で売却します。

単独の price ディレクティブは、市場評価のための参照データを提供します。Live Prices は、ホストされた台帳内の対応資産についてこれらのディレクティブを維持できます。更新しても、ロット、ブッキングメソッド、取得原価、記録された売却収入は変わりません。

価格使用ルール​

  1. 価格注釈(@)は、どのロットがブッキングされるかに影響しません。ロットマッチングは、もっぱら取得原価({})と口座のブッキングメソッドによって処理されます。
  2. @ 記号は、次の目的にのみ使用されます。
  • 通貨換算。
  • 取引時点の資産の市場価値の記録。
  • キャピタルゲイン計算のための売却価格の提供。

設定​

ブッキングメソッドは、グローバルまたは口座ごとに設定できます。

グローバル記帳方法​

option ディレクティブを使って、Beancount ファイル全体のデフォルトのブッキングメソッドを設定できます。

option "booking_method" "STRICT"

受け付けられる値は "STRICT"(何も設定しない場合のデフォルト)、"STRICT_WITH_SIZE"、"NONE"、"FIFO"、"LIFO"、"HIFO"、"AVERAGE" です。その他の文字列は、ロード時に Error for option 'booking_method' で拒否されます。"AVERAGE" はここでも open でも受け付けられますが、それで減少をブッキングすると失敗します。上記の AVERAGE セクションを参照してください。

アカウント単位の上書き設定​

口座ごとに異なるメソッドを持つことがしばしば有用です。例えば、退職口座には FIFO を使いたいが、課税対象の証券口座には特定の税務ロットを売却することを確実にするために STRICT を使いたい場合があります。口座を開設するときにブッキングメソッドを設定できます。

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

ベストプラクティス​

  1. 在庫の整理: 台帳を clean かつシンプルに保つために、保有する各固有の商品に対して個別の口座を使用し、それぞれを 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 は stray なポスティングを拒否し、2つの在庫を黙って混在させることはありません。

  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 は在庫の状態を検査するツールを提供します。
  • 在庫状態の検査: bea doctor context main.beancount 42 を使って、42行目の取引を、そのポスティングと影響を受ける口座残高を含めて検査します。ファイル名と行番号は、検査したい取引に置き換えてください。

    <LINENO> を取引の直後の行番号に置き換えると、その効果を確認できます。

  • ロットマッチングの検証: bea check ツールはファイル全体を検証します。STRICT モードでの曖昧なロットマッチなど、あらゆるブッキングエラーを検出します。

出典: https://beancount.io/ja/docs/Basics/inventories