Към основното съдържание

Партиди в Beancount, себестойност и методи за осчетоводяване

Как Beancount осчетоводява партиди при продажба на акции или валути: себестойност, спецификации на партидите, STRICT, FIFO, LIFO и HIFO осчетоводяване, цена спрямо себестойност, настройки за отделни сметки.

Системата за инвентар на Beancount е мощна функция за проследяване на активи, които се купуват и продават с времето, като акции, взаимни фондове или чуждестранни валути. Тя позволява прецизно проследяване на себестойността, което е от съществено значение за изчисляване на капиталови печалби и разбиране на представянето на портфейла. Този урок обхваща основните механики на управлението на инвентари във вашия регистър.

Основни концепции​

В основата си управлението на инвентара се върти около проследяването на позиции. "Позиция" е просто количество от дадена стока, държано в сметка. Beancount разграничава два основни типа позиции.

Видове позиции​

  1. Проста позиция (без себестойност): Това е стандартно записване на баланс. Представлява количество от дадена стока без свързана цена на придобиване. Подходящо е за парични средства или прости проверки на баланса.

    Assets:Bank:Checking      100.00 USD
  2. Позиция със себестойност: Този тип позиция включва не само броя единици и стоката, но и цената, на която е придобита. Това е основата на проследяването на инвентара. Себестойността се указва във фигурни скоби {}.

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

    В този пример държим 10 единици от VTSAX. Всяка единица е придобита на цена от $100.00 USD. Тази конкретна партида акции е идентифицирана като "партида" (lot).

Операции с инвентара​

Има две основни операции, които можете да извършите върху инвентар:

  1. Увеличения (Добавяне към инвентара): Когато купувате стока, вие увеличавате инвентара си. Създавате нова партида с определен брой единици и себестойност.

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

    Тук купуваме 50 единици от STOCK на цена от $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 единици от STOCK от партидата, която е закупена на $25.00 USD за единица.

Методи за осчетоводяване (Booking Methods)​

Когато намалявате инвентар, Beancount се нуждае от правило, за да реши коя конкретна партида да изтегли, ако няколко партиди съвпадат с намалението. Това правило се нарича "метод на резервиране". Можете да зададете стойност по подразбиране за целия файл с опция или да дадете на дадена сметка свой собствен метод в директивата open.

Beancount 3.2.3 приема седем имена на методи: STRICT (по подразбиране), STRICT_WITH_SIZE, NONE, FIFO, LIFO, HIFO и AVERAGE. Шест от тях са реализирани; 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 съобщава 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 върху едни и същи партиди​

Трите метода се различават само когато най-старата, най-новата и най-скъпата партида са три различни партиди. Този регистър подрежда точно това — партида 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_SIZE е STRICT плюс един допълнителен критерий за разрешаване: когато няколко партиди съвпадат, но точно една от тях държи точно броя единици, които премахвате, тази партида се избира.

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

Това записва $210.00 печалба срещу партидата от $120.00. Идентичният файл с "STRICT" на реда open се проваля с 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 обработва стоки.

Спецификация на партидата​

"Партида" е специфичен блок от дадена стока, придобит в определен момент и на определена цена. Когато създавате или намалявате позиция, можете да укажете подробно нейните атрибути на партидата.

Пълна спецификация​

Когато увеличавате инвентар (купувате), можете да укажете до три атрибута за партидата, разделени със запетаи в една двойка фигурни скоби:

Assets:Invest:STOCK  10 STOCK {100.00 USD, 2024-01-15, "lot-identifier"}
  • 100.00 USD — себестойността, изразена за единица.
  • 2024-01-15 — датата на придобиване. Beancount я попълва от датата на транзакцията, когато я пропуснете, поради което съобщенията за грешки по-горе показват дата на всяка партида.
  • "lot-identifier" — незадължителен низов етикет.

Въпреки че и трите са незадължителни, предоставянето поне на себестойността е стандартна практика. Фигурните скоби трябва да останат на един ред, а коментарите в регистъра започват с ;, никога с #.

Методи за съвпадение​

Когато намалявате инвентар (продавате), използвате същия синтаксис, за да укажете от коя партида(и) да продавате.

  • Съвпадение по себестойност: Това е най-разпространеният метод.

    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

    Този запис продава 10 STOCK от партидата, която е струвала $100.00 всяка, на продажна цена от $105.00 всяка.

Самостоятелна директива price предоставя референтни данни за пазарна оценка. Цени на живо могат да поддържат тези директиви за поддържани активи в хоствани регистри. Обновяването оставя вашите партиди, метод на резервиране, разходи за придобиване и записани приходи от продажба непроменени.

Правила за използване на цената​

  1. Анотациите на цени (@) не влияят на това коя партида се резервира. Съвпадението на партиди се обработва изключително от себестойността ({}) и метода на резервиране на сметката.
  2. Символът @ се използва само за:
  • Валутни конверсии.
  • Записване на пазарната стойност на актив към момента на транзакцията.
  • Предоставяне на продажната цена за изчисления на капиталови печалби.

Конфигурация​

Можете да конфигурирате методи за резервиране глобално или за отделна сметка.

Глобален метод на осчетоводяване​

Можете да зададете метод на резервиране по подразбиране за целия си Beancount файл с директивата option.

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. Организация на инвентара: За да поддържате регистъра си чист и прост, силно се препоръчва да използвате отделни сметки за всяка уникална стока, която държите, и да ограничите всяка от тях до тази стока в нейната директива 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 предоставя инструменти за проверка на състоянието на вашия инвентар.
  • Преглед на състоянието на инвентара: Използвайте bea doctor context main.beancount 42, за да прегледате транзакцията на ред 42, включително нейните записвания и засегнатите баланси на сметките. Заменете името на файла и номера на реда с транзакцията, която искате да прегледате.

    Заменете <LINENO> с номера на реда точно след транзакция, за да видите ефекта ѝ.

  • Проверка на съвпадението на партиди: Инструментът bea check валидира целия ви файл. Той ще улови всякакви грешки при резервиране, като например нееднозначни съвпадения на партиди в режим STRICT.

Източник: https://beancount.io/bg/docs/Basics/inventories