Beancountの在庫システムは、株式、投資信託、外国通貨など、時間をかけて売買される資産を追跡するための強力な機能です。これは、キャピタルゲインの計算やポートフォリオのパフォーマンスを理解するために不可欠な、原価基準の正確な追跡を可能にします。このチュートリアルでは、元帳で在庫を管理するための核となる仕組みを説明します。
基本概念
在庫管理の中心は、ポジションの追跡です。「ポジション」とは、単にアカウント内で保有されている商品の数量のことです。Beancountは、2つの基本的なタイプのポジションを区別します。
ポジションのタイプ
-
単純ポジション(原価なし):これは標準的な残高転記です。取得原価を伴わない商品の数量を表します。現金や単純な残高照合に適しています。
Assets:Bank:Checking 100.00 USD -
原価基準付きポジション:このタイプのポジションには、ユニット数と商品に加えて、取得時の原価も含まれます。これが在庫追跡の基礎です。原価は中括弧
{}内で指定されます。Assets:Invest:VTSAX 10 VTSAX {100.00 USD, "lot-1"}この例では、
VTSAXを10ユニット保有しています。各ユニットは $100.00 USD で取得されました。この特定の株式の塊は「ロット」として識別されます。
在庫操作
在庫に対して実行できる主な操作は2つあります:
-
増加(在庫への追加):商品を購入すると、在庫が増加します。特定のユニット数と原価基準を持つ新しいロットを作成します。
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アカウントにロットが作成されます。 -
減少(在庫からの除去):商品を売却すると、在庫が減少します。どのロットから売却するかを指定する必要があります。これは、中括弧内に一致する情報を提供することで行われます。
2024-01-20 * "Sell shares" Assets:Invest:STOCK -25 STOCK {25.00 USD} Assets:Bank:Checking 625.00 USDこの取引では、1ユニットあたり $25.00 USD で購入したロットから
STOCKを25ユニット売却しています。
ブッキングメソッド
在庫を減少させるとき、複数のロットが減少条件に一致する場合、Beancountはどの特定のロットから引き落とすかを決定するためのルールを必要とします。このルールは「ブッキングメソッド」と呼ばれます。ファイル全体のデフォルトをオプションで設定したり、open ディレクティブでアカウントごとに独自のメソッドを指定したりできます。
Beancount 3.2.3 では、STRICT(デフォルト)、STRICT_WITH_SIZE、NONE、FIFO、LIFO、HIFO、AVERAGE の7つのメソッド名が受け入れられます。そのうち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:GainsBeancount は 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つのメソッドが異なるのは、最も古いロット、最も新しいロット、最も高価なロットがすべて異なる場合のみです。この元帳はまさにそれを設定しています(ロット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:Fifo | FIFO | ロットA、2024-01-10 | $100.00 | $500.00 | 10 @ $120.00,10 @ $90.00 |
Assets:Broker:Lifo | LIFO | ロットC、2024-03-10 | $90.00 | $600.00 | 10 @ $100.00,10 @ $120.00 |
Assets:Broker:Hifo | HIFO | ロットB、2024-02-10 | $120.00 | $300.00 | 10 @ $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つの属性を、単一の中括弧のペア内にカンマ区切りで指定できます:
Assets:Invest:STOCK 10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}100.00 USD— 原価基準で、ユニットあたりで表されます。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つのシナリオは次のとおりです:
-
価格注釈(換算):
@を使用して、ある通貨から別の通貨に換算します。Assets:Forex 1000 USD @ 0.85 EUR -
原価基準(取得):資産を購入するときに
{}を使用してその原価を確定します。Assets:Invest 10 STOCK {100.00 USD} -
両方(売却価格の記録付き売却):資産を売却するとき、
{}を使用して売却するロットを特定し、@を使用して売却価格を記録します。これにより、自動的なキャピタルゲインの計算が可能になります。Assets:Invest -10 STOCK {100.00 USD} @ 105.00 USDこのエントリは、1株あたり $100.00 の原価のロットから
STOCKを10株、1株あたり $105.00 の売却価格で売却します。
価格の使用ルール
- 価格注釈(
@)は、どのロットがブッキングされるかに影響しません。ロットマッチングは、原価基準({})とアカウントのブッキングメソッドによって排他的に処理されます。 @記号は以下の場合にのみ使用されます:
- 通貨換算。
- トランザクション時点での資産の市場価値の記録。
- キャピタルゲイン計算のための売却価格の提供。
設定
ブッキングメソッドは、グローバルまたはアカウントごとに設定できます。
グローバルなブッキングメソッド
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"ベストプラクティス
-
在庫の整理:元帳をクリーンでシンプルに保つために、保有する各固有の商品に対して別々のアカウントを使用し、
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%
- デバッグ:エラーや予期しない動作が発生した場合、Beancountは在庫の状態を検査するためのツールを提供します。
-
在庫状態の調査:
bean-doctorツールを使用すると、ファイル内の任意の時点でのすべての在庫の正確な状態を表示できます。<LINENO>を、トランザクションの直後の行番号に置き換えて、その効果を確認します。 -
ロットマッチングの検証:
bean-checkツールはファイル全体を検証します。STRICTモードでの曖昧なロットマッチなど、ブッキングエラーをすべて検出します。