Naar hoofdinhoud springen

Beancount lots, kostprijs en boekingsmethoden

Hoe Beancount lots boekt wanneer je aandelen of valuta verkoopt: kostprijs, lotspecificaties, STRICT, FIFO, LIFO en HIFO boeking, prijs versus kostprijs, overschrijvingen per rekening.

Beancounts voorraadsysteem is een krachtige functie voor het volgen van activa die in de loop van de tijd worden gekocht en verkocht, zoals aandelen, beleggingsfondsen of vreemde valuta's. Het maakt nauwkeurige tracking van de kostprijs mogelijk, wat essentieel is voor het berekenen van vermogenswinsten en het begrijpen van portefeuilleprestaties. Deze tutorial behandelt de kernmechanismen van het beheren van voorraden in je grootboek.

Kernbegrippen​

In essentie draait voorraadbeheer om het volgen van posities. Een "positie" is simpelweg een hoeveelheid van een commodity die in een rekening wordt aangehouden. Beancount onderscheidt twee fundamentele soorten posities.

Positietypen​

  1. Eenvoudige positie (geen kostprijs): Dit is een standaard balansboeking. Het vertegenwoordigt een hoeveelheid van een commodity zonder enige bijbehorende aanschafkosten. Het is geschikt voor contant geld of eenvoudige balansasserties.

    Assets:Bank:Checking      100.00 USD
  2. Positie met kostprijs: Dit type positie bevat niet alleen het aantal eenheden en de commodity, maar ook de kostprijs waartegen deze is verworven. Dit is de basis van voorraadtracking. De kostprijs wordt gespecificeerd binnen accolades {}.

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

    In dit voorbeeld houden we 10 eenheden van VTSAX aan. Elke eenheid is verworven tegen een kostprijs van $100,00 USD. Deze specifieke batch aandelen wordt geïdentificeerd als een "lot".

Voorraadbewerkingen​

Er zijn twee primaire bewerkingen die je op een voorraad kunt uitvoeren:

  1. Augmentaties (toevoegen aan voorraad): Wanneer je een commodity koopt, vul je je voorraad aan. Je creëert een nieuw lot met een specifiek aantal eenheden en een kostprijs.

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

    Hier kopen we 50 eenheden van STOCK tegen een kostprijs van $25,00 USD per eenheid. Dit creëert een lot in de rekening Assets:Invest:STOCK.

  2. Reducties (verwijderen uit voorraad): Wanneer je een commodity verkoopt, verminder je je voorraad. Je moet specificeren uit welk lot je verkoopt. Dit doe je door overeenkomende informatie in de accolades op te geven.

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

    In deze transactie verkopen we 25 eenheden van STOCK uit het lot dat is gekocht tegen $25,00 USD per eenheid.

Boekingmethoden​

Wanneer je een voorraad reduceert, heeft Beancount een regel nodig om te beslissen uit welk specifiek lot te putten als meerdere loten overeenkomen met de reductie. Deze regel wordt de "bookingsmethode" genoemd. Je kunt een standaard instellen voor het hele bestand met een optie, of één rekening zijn eigen methode geven op de open-richtlijn.

Beancount 3.2.3 accepteert zeven methodenamen: STRICT (de standaard), STRICT_WITH_SIZE, NONE, FIFO, LIFO, HIFO en AVERAGE. Zes ervan zijn geïmplementeerd; AVERAGE wordt geparseerd maar geeft een fout op het moment dat het een reductie moet boeken, zoals de sectie AVERAGE hieronder laat zien.

1. STRICT (Standaard)​

De STRICT-methode is de standaard en de veiligste bookingsmethode. Het dwingt expliciete en ondubbelzinnige matching af.

2024-01-01 open Assets:Invest:STOCK "STRICT"
  • Vereist exacte lotovereenkomst: De kostenspecificatie van de reductieboeking ({...}) moet één enkel lot identificeren — op kostprijs, op aanschafdatum, op label, of op een combinatie daarvan.
  • Fouten bij dubbelzinnige overeenkomsten: Als de specificatie overeenkomt met meer dan één lot, geeft Beancount een AmbiguousMatchError in plaats van te gokken.
  • Uitzondering: Als een reductie precies het totale aantal eenheden verwijdert dat de specificatie matcht, is een lege specificatie ({}) toegestaan, en de reductie wordt verdeeld over die loten.

Dit grootboek bevat twee loten en verkoopt er één door de kostprijs te benoemen, wat ondubbelzinnig is:

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

Het laadt zonder fouten, boekt $300,00 winst naar Income:Gains, en laat 10 STK {100.00 USD} in de rekening achter.

Vervang die laatste boeking door een lege specificatie en hetzelfde bestand faalt:

; 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 rapporteert Ambiguous matches for "-10 STK {}" en somt de kandidaten op. De hele positie verkopen is echter prima, omdat er niets meer te kiezen valt:

; 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

Dat boekt $800,00 winst — $3.000,00 opbrengst tegen $1.000,00 + $1.200,00 kostprijs — en laat de rekening leeg achter. Dit is een eigenschap van STRICT zelf, niet iets waarvoor je naar STRICT_WITH_SIZE moet overschakelen.

2. FIFO (First-In, First-Out)​

De FIFO-methode boekt reducties automatisch tegen de oudste beschikbare loten eerst.

2024-01-01 open Assets:Invest:STOCK "FIFO"
  • Automatische resolutie: Het lost dubbelzinnigheid op door de oudste overeenkomende loten te selecteren.
  • Chronologische matching: Je gaat ervan uit dat je de activa verkoopt die je het langst hebt aangehouden. Verschillende belastingautoriteiten behandelen dit als de standaard wanneer je geen lot hebt geïdentificeerd.

3. LIFO (Last-In, First-Out)​

De LIFO-methode is het tegenovergestelde van FIFO. Het boekt reducties tegen de nieuwste beschikbare loten eerst.

2024-01-01 open Assets:Invest:STOCK "LIFO"
  • Omgekeerde chronologische volgorde: Het selecteert de meest recent verworven overeenkomende loten.
  • Nieuwste, niet duurste: LIFO kiest alleen op basis van aanschafdatum. Het verkoopt toevallig de aandelen met de hoogste kostprijs wanneer de prijzen zijn gestegen, maar als je nieuwste lot je goedkoopste is — wat het onderstaande voorbeeld wil aantonen — realiseert LIFO de grootste winst, niet de kleinste. De methode die altijd de duurste aandelen verkoopt is HIFO, hierna beschreven.

4. HIFO (Highest-In, First-Out)​

De HIFO-methode boekt reducties tegen de duurste beschikbare loten eerst, ongeacht hun datum.

2024-01-01 open Assets:Invest:STOCK "HIFO"
  • Op kostprijs gerangschikte matching: Het selecteert de overeenkomende loten met de hoogste kostprijs.
  • Kleinste gerealiseerde winst: Voor een gegeven verkoopprijs realiseert het verkopen van de aandelen met de hoogste kostprijs de kleinste winst (of het grootste verlies). Of je het mag gebruiken is een jurisdictievraag — in de Verenigde Staten bijvoorbeeld vereist het kiezen van een lot überhaupt specifieke identificatie op het moment van verkoop — dus behandel de methode als een boekhoudmechanisme en bevestig de belastingkeuze afzonderlijk.

5. Vergelijking van FIFO, LIFO en HIFO op dezelfde lots​

De drie methoden verschillen alleen wanneer het oudste, het nieuwste en het duurste lot drie verschillende loten zijn. Dit grootboek regelt precies dat — lot A is het oudste, lot C is het nieuwste, en het middelste lot B is het duurste — en verkoopt vervolgens 10 aandelen uit drie rekeningen die alleen in hun bookingsmethode verschillen:

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

Het laadt met nul fouten en boekt in totaal $1.400,00 winst, als volgt verdeeld:

RekeningMethodeGeboekt lotKostprijsGerealiseerde winstResterende loten
Assets:Broker:FifoFIFOlot A, 2024-01-10$100,00$500,0010 @ $120,00, 10 @ $90,00
Assets:Broker:LifoLIFOlot C, 2024-03-10$90,00$600,0010 @ $100,00, 10 @ $120,00
Assets:Broker:HifoHIFOlot B, 2024-02-10$120,00$300,0010 @ $100,00, 10 @ $90,00

De LIFO-rij is het bekijken waard: het realiseerde de grootste winst van de drie, omdat het nieuwste lot ook het goedkoopste was.

6. STRICT_WITH_SIZE​

STRICT_WITH_SIZE is STRICT plus één extra tie-breaker: wanneer meerdere loten overeenkomen maar precies één ervan precies het aantal eenheden bevat dat je verwijdert, wordt dat lot gekozen.

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

Dat boekt $210,00 winst tegen het lot van $120,00. Het identieke bestand met "STRICT" op de open-regel faalt met Ambiguous matches for "-7 STK {}".

7. GEMIDDELDE (geaccepteerd, maar niet geïmplementeerd)​

AVERAGE is een geldige naam — option "booking_method" "AVERAGE" en open … "AVERAGE" worden beide geparseerd — maar Beancount 3.2.3 heeft er geen implementatie achter. Alles hier laadt tot de verkoop:

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

Op het moment dat die reductie geboekt moet worden, stopt de loader met:

AVERAGE method is not supported

Plan hier geen grootboek omheen. Als je vandaag gemiddelde-kostprijsgedrag wilt, houd de positie dan in een NONE-rekening en bereken het gemiddelde zelf, of volg elk lot en accepteer winsten op lotniveau.

8. GEEN​

De NONE-methode schakelt lotmatching volledig uit.

2024-01-01 open Assets:Invest:STOCK "NONE"
  • Geen lotmatching: Beancount probeert reducties niet aan augmentaties te koppelen.
  • Staat gemengde tekens toe: Dit staat een rekening toe om zowel positieve als negatieve saldi van dezelfde commodity tegelijkertijd aan te houden. Dit gedrag is vergelijkbaar met hoe de Ledger CLI-tool commodities behandelt.

Lotspecificatie​

Een "lot" is een specifiek blok van een commodity dat op een bepaald tijdstip en tegen een bepaalde prijs is verworven. Wanneer je een positie creëert of reduceert, kun je de lotattributen in detail specificeren.

Volledige specificatie​

Bij het augmenteren van een voorraad (kopen), kun je tot drie attributen voor het lot specificeren, kommagescheiden binnen één paar accolades:

Assets:Invest:STOCK  10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}
  • 100.00 USD — de kostprijs, uitgedrukt per eenheid.
  • 2024-01-15 — de aanschafdatum. Beancount vult deze in vanuit de transactiedatum wanneer je die weglaat, wat de reden is dat de bovenstaande foutmeldingen een datum bij elk lot tonen.
  • "lot-identifier" — een optioneel tekenreekslabel.

Hoewel alle drie optioneel zijn, is het opgeven van ten minste de kostprijs standaardpraktijk. De accolades moeten op één regel blijven, en opmerkingen in een grootboek beginnen met ;, nooit met #.

Matchingsmethoden​

Bij het reduceren van een voorraad (verkopen), gebruik je dezelfde syntaxis om te specificeren uit welk(e) lot(en) je verkoopt.

  • Matchen op kostprijs: Dit is de meest gebruikte methode.

    Assets:Invest:STOCK  -5 STOCK {100.00 USD}
  • Matchen op datum: Als de kostprijzen identiek zijn, kun je onderscheiden met behulp van de aanschafdatum.

    Assets:Invest:STOCK  -5 STOCK {2024-01-15}
  • Matchen op label: Labels bieden een waterdichte manier om een lot te identificeren.

    Assets:Invest:STOCK  -5 STOCK {"lot-identifier"}
  • Het lot overlaten aan de bookingsmethode: Een lege set accolades {} benoemt geen lot, dus de bookingsmethode van de rekening kiest. Onder FIFO, LIFO of HIFO is dat het oudste, nieuwste of duurste overeenkomende lot; onder de standaard STRICT is het een AmbiguousMatchError, tenzij de reductie de overeenkomende loten precies leegmaakt.

    Assets:Invest:STOCK  -5 STOCK {}

Prijsverwerking​

Het is cruciaal om het verschil te begrijpen tussen kostprijs ({}) en prijs (@). Ze dienen verschillende doelen en zijn niet uitwisselbaar.

Prijs vs Kosten​

  • {cost}: Definieert de aanschafkosten van een actief. Het maakt deel uit van het voorraadlot zelf en wordt gebruikt voor het boeken van reducties en het berekenen van vermogenswinsten.
  • @ price: Een annotatie die een marktprijs vastlegt op het moment van een transactie. Het wordt gebruikt voor valutaconversies of om de marktwaarde op een bepaalde datum te noteren.

Hier zijn de drie scenario's:

  1. Prijsannotatie (conversie): Gebruik @ om van de ene valuta naar de andere te converteren.

    Assets:Forex     1000 USD @ 0.85 EUR
  2. Kostprijs (aanschaf): Gebruik {} bij het kopen van een actief om de kostprijs vast te stellen.

    Assets:Invest    10 STOCK {100.00 USD}
  3. Beide (verkoop met prijsregistratie): Gebruik bij het verkopen van een actief {} om het verkochte lot te identificeren en @ om de verkoopprijs vast te leggen. Dit maakt geautomatiseerde berekening van vermogenswinsten mogelijk.

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

    Deze boeking verkoopt 10 STOCK uit het lot dat $100,00 per stuk kostte, tegen een verkoopprijs van $105,00 per stuk.

Een op zichzelf staande price-richtlijn levert referentiegegevens voor marktwaardering. Live prijzen kunnen deze richtlijnen onderhouden voor ondersteunde activa in gehoste grootboeken. Een verversing laat je loten, bookingsmethode, aanschafkosten en vastgelegde verkoopopbrengsten onveranderd.

Regels voor Prijsgebruik​

  1. Prijsannotaties (@) beïnvloeden niet welk lot wordt geboekt. Lotmatching wordt uitsluitend afgehandeld door de kostprijs ({}) en de bookingsmethode van de rekening.
  2. Het @-symbool wordt alleen gebruikt voor:
  • Valutaconversies.
  • Het vastleggen van de marktwaarde van een actief op het moment van een transactie.
  • Het leveren van de verkoopprijs voor berekeningen van vermogenswinsten.

Configuratie​

Je kunt bookingsmethoden globaal of per rekening configureren.

Globale Boekingswijze​

Je kunt een standaard bookingsmethode voor je hele Beancount-bestand instellen met de option-richtlijn.

option "booking_method" "STRICT"

De geaccepteerde waarden zijn "STRICT" (de standaard wanneer je niets instelt), "STRICT_WITH_SIZE", "NONE", "FIFO", "LIFO", "HIFO" en "AVERAGE". Elke andere tekenreeks wordt bij het laden afgewezen met Error for option 'booking_method'. "AVERAGE" wordt hier en op open geaccepteerd, maar het boeken van een reductie ermee faalt, zoals de sectie AVERAGE hierboven laat zien.

Per-Rekening Override​

Het is vaak nuttig om verschillende methoden voor verschillende rekeningen te hebben. Je wilt bijvoorbeeld FIFO voor een pensioenrekening maar STRICT voor een belastbare brokerage-rekening om er zeker van te zijn dat je specifieke belastingloten verkoopt. Je kunt de bookingsmethode instellen wanneer je de rekening opent.

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

Best Practices​

  1. Voorraadorganisatie: Om je grootboek schoon en eenvoudig te houden, is het sterk aanbevolen om aparte rekeningen te gebruiken voor elke unieke commodity die je aanhoudt, en elke rekening te beperken tot die commodity op de open-richtlijn.

    ; 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

    Vermijd het mixen van verschillende aandelen of fondsen in dezelfde rekening, omdat dit voorraadbeheer bemoeilijkt. De commoditylijst op open zorgt ervoor dat Beancount een verdwaalde boeking afwijst in plaats van stilzwijgend twee voorraden te mixen.

  2. Lotbeheer:

  • Gebruik betekenisvolle labels voor loten, vooral voor specifieke transacties zoals tax-loss harvesting of aandelenbonussen van werknemers.

    Assets:Invest:STOCK  10 STOCK {100.00 USD, "tax-loss-harvest-2024"}
  • Documenteer je transacties met opmerkingen. Dit maakt je grootboek later gemakkelijker te lezen en te begrijpen.

    Assets:Invest:STOCK  -10 STOCK {100.00 USD} @ 110.00 USD ; Gain: 10%
  1. Debuggen: Als je fouten of onverwacht gedrag tegenkomt, biedt Beancount tools om de status van je voorraad te inspecteren.
  • Voorraadstatus onderzoeken: Gebruik bea doctor context main.beancount 42 om de transactie op regel 42 te inspecteren, inclusief de boekingen en de betrokken rekeningsaldi. Vervang de bestandsnaam en het regelnummer door de transactie die je wilt inspecteren.

    Vervang <LINENO> door het regelnummer direct na een transactie om het effect ervan te zien.

  • Lotmatching verifiëren: De bea check-tool valideert je hele bestand. Het zal eventuele boekingsfouten opsporen, zoals dubbelzinnige lotovereenkomsten in STRICT-modus.

Bron: https://beancount.io/nl/docs/Basics/inventories