Thanks Ev2geny, your tool is interesting, and I might borrow some code from it.
My need is more complex, as this tax is about unrealized capital gains, but it is not calculated on them.
- The prices used to calculate the unrealized gain are the /Rücknahmepreis/ (Net Asset Value, or NAV) published directly by the fund provider. These differ from the market price, which includes bid/ask spreads. This data is not—and has no reason to be—in my ledger.
- The dates used are the first and last business days of the year, even if you purchased shares mid-year. The amount is then discounted for the months preceding the purchase. This is also not in my ledger.
- The taxed amount is the minimum of the unrealized gain and a "base yield"—essentially what you would gain if the investment appreciated at the Basiszins (a rate published yearly by the Bundesministerium der Finanzen) multiplied by 0.7. This rate is not in my ledger either.
The above is a simplification; the German method is one of the most complex ways to mitigate tax deferrals and is largely irrelevant for non-residents. Once you understand the method, it's easy to build in a spreadhseet, but hard to scale for all positions and lots held by the typical investor with regular trades.
I will probably process the data in an org-babel notebook with ob-beanquery to get year-end inventories and holding periods, then process that with a script to consider all external inputs such as base yield. Or, I'll explore Marimo, following your good work on the beanquery documentation.
Keep up the good work.
--Stefano