Multi-currency investments and Section 988 gains

54 views
Skip to first unread message

SomeCallMeTim

unread,
Mar 11, 2026, 6:29:34 AMMar 11
to Beancount

I’ve been working on a pattern to track:

* EUR cash lots with USD basis (for U.S. section 988-style FX gain/loss)
* A stock traded in EUR, with cost basis in EUR (not USD)
* Capital gains in both EUR and USD for tax reporting
* FX gain only when EUR is actually disposed (when a stock is bought with EUR, or when a EUR to USD conversion occurs)

I’d really appreciate feedback on whether this is idiomatic Beancount and if there are cleaner ways to express the same economics.

Below is a complete, runnable example with prices/rates chosen to keep the math simple.  I’d love any feedback on more elegant patterns, potential pitfalls, or ways to simplify this while still keeping explicit EUR lots, EUR-based stock cost, and U.S. tax reporting needs (section 988-style FX plus capital gains in both currencies).

option "operating_currency" "USD"

1970-01-01 open Assets:US:Checking
1970-01-01 open Assets:EU:Checking
1970-01-01 open Assets:IBKR:BVME:IMBL
  name: "Internazionale Imobiliare - Milan Exchange"
1970-01-01 open Income:US:FX:Sec988
1970-01-01 open Income:CapGains:USD
1970-01-01 open Income:CapGains:EUR
1970-01-01 open Equity:Tax:CapGains:Bridge
1970-01-01 open Equity:Opening-Balances

; ----------------------------------------------------------------------
; EUR/USD Price Table
; ----------------------------------------------------------------------

2026-02-01 price EUR 1.20 USD
2026-03-01 price EUR 1.15 USD
2026-04-01 price EUR 1.25 USD
2026-05-01 price EUR 1.30 USD
2026-06-01 price EUR 1.40 USD

; ----------------------------------------------------------------------
; Initial funding in USD
; ----------------------------------------------------------------------

2026-01-01 * "Initial funding of US Checking"
  Assets:US:Checking              5000.00 USD
  Equity:Opening-Balances        -5000.00 USD

; ----------------------------------------------------------------------
; Buy EUR in two lots (these EUR lots later fund the IMBL buy)
;   Lot 1: 500 EUR for 600 USD -> 1.20 USD/EUR
;   Lot 2: 500 EUR for 575 USD -> 1.15 USD/EUR
;   Combined EUR basis: 1175 USD
; ----------------------------------------------------------------------

2026-02-01 * "Buy EUR lot 1"
  Assets:EU:Checking              500 EUR {"20260201"} @@ 600.00 USD
  Assets:US:Checking             -600 USD

2026-03-01 * "Buy EUR lot 2"
  Assets:EU:Checking              500 EUR {"20260301"} @@ 575.00 USD
  Assets:US:Checking             -575 USD

; ----------------------------------------------------------------------
; Buy 100 IMBL with 1000 EUR (10 EUR/sh)
;   At trade time, 1 EUR = 1.25 USD, so 1000 EUR = 1250 USD.
;   Section 988 gain on EUR used: 1250 - 1175 = 75 USD.
;   IMBL is kept with EUR cost; FX gain goes to Income:US:FX:Sec988.
; ----------------------------------------------------------------------

2026-04-01 * "Buy 100 IMBL with EUR, realize FX gain on EUR lots"
  Assets:EU:Checking             -500 EUR {"20260201"} @ 1.25 USD
  Assets:EU:Checking             -500 EUR {"20260301"} @ 1.25 USD
  Assets:EU:Checking              1000 EUR @@ 1250 USD
  Income:US:FX:Sec988            -75 USD
  Assets:IBKR:BVME:IMBL           100 IMBL {"20260401"} @@ 1000 EUR
  Assets:EU:Checking             -1000 EUR

; ----------------------------------------------------------------------
; Sell the 100 IMBL for EUR, no immediate 988 gain
;   Price: 11 EUR/sh -> proceeds 1100 EUR.
;   EUR/USD = 1.30, so proceeds get a new USD basis 1430.
;   Capital gain: 100 EUR, also tracked in USD (130 USD) for tax.
; ----------------------------------------------------------------------

2026-05-01 * "Sell 100 IMBL for EUR, record EUR capital gain"
  Assets:IBKR:BVME:IMBL          -100 IMBL {"20260401"}
  Assets:EU:Checking              1100.00 EUR
  Income:CapGains:EUR            -100.00 EUR
  Assets:EU:Checking             -1100.00 EUR @@ 1430.00 USD
  Assets:EU:Checking              1100 EUR {"20260501"} @@ 1430.00 USD
  Income:CapGains:USD            -130 USD @@ 100.00 EUR
  Equity:Tax:CapGains:Bridge      100 EUR

; ----------------------------------------------------------------------
; Later EUR -> USD conversion (where section 988 is realized)
;   Assume one month later, 1 EUR = 1.40 USD.
;   EUR disposed: 1100 EUR
;   USD received: 1540 USD
;   FX gain: 1540 - 1430 = 110 USD (section 988-style).
; ----------------------------------------------------------------------

2026-06-01 * "Convert 1100 EUR back to USD, realize section 988 gain"
  Assets:EU:Checking             -1100 EUR {"20260501"}
  Income:US:FX:Sec988            -110.00 USD
  Assets:US:Checking              1540.00 USD


Thanks in advance...

Chary Ev2geny

unread,
Mar 12, 2026, 9:51:45 AMMar 12
to Beancount
Hi, there!

disclaimer: I am not an accountant and I just learned about the Section 988 gains after I have read your post. But I thought that this may be related to the sing_curr_conv which I created.

As far as I see, what you recorded makes sense. But I am just thinking that the issue here is that you try to combine a simple transaction record as well as post transaction tax analytics in one place.

In a real world (personal or corporate) this would not be in one place. In a corporate world some corporation would just go around doing business in a local currency and then some specially trained person would then do some clever analytics to calculate the exchange -rate fluctuation - related   gains in a functional currency. And this analytics would also depend on the on the source of exchange rate the person uses (so a bit subjective thing)

I think the best way to use beancount is to do the same: you just record transactions they way you did them, and then you do a later analytics to calculate all the difficult to calculate gains. May be you even outsource this to somebody else.

I am sorry I do not have straight answer to your question, but I will think how I would approach this.

SomeCallMeTim

unread,
Mar 12, 2026, 3:27:51 PMMar 12
to Beancount
Thanks for the feedback.  Yes, it would be preferred if I could just do something like:

2026-04-01 * "Buy 100 IMBL with EUR"

  Assets:EU:Checking             -1000 EUR
  Assets:IBKR:BVME:IMBL           100 IMBL {"20260401"} @@ 1000 EUR

However this then creates a balance sheet that looks like:

beanquery> SELECT account, sum(position) WHERE date <= 2026-04-01 AND account ~ 'Asset' GROUP BY account
Assets:EU:Checking       500 EUR { 1.20 USD}    500 EUR { 1.15 USD}  -1000 EUR

I'm not sure if that's a problem per-se.  I suppose a follow-on parsing script could handle this.  Since the disposal occurred in euro, there's no precise exchange rate, I can just use whatever tax authority exchange rate is needed.

On the actual currency conversions, I do need to specify the precise exchange rate because the price I get from the bank/brokerage at the instant of the conversion may be different from the daily published spot rate.  I suppose I could not use lots and instead do something like:

2026-02-01 * "Buy EUR"
  Assets:EU:Checking              500 EUR @@ 600.0 USD

  Assets:US:Checking              -600 USD

This would make the balance sheet query cleaner, but would require a parsing script to find all the EUR/USD currency conversions and calculate the section 988 gains automatically.

I'm curious if anyone is actually doing something like this in practice.
Reply all
Reply to author
Forward
0 new messages