Price Annotations¶
Overview¶
Price annotations record the market exchange rate at the time of a transaction. Unlike cost specifications, prices are informational and typically don't affect transaction balancing when a cost is also present.
Syntax¶
price_annotation = "@" amount ; Per-unit price
| "@@" amount ; Total price
Price Types¶
Per-Unit Price @¶
Specifies the price per unit:
; 100 EUR at 1.10 USD each
Assets:EUR 100 EUR @ 1.10 USD
Total Price @@¶
Specifies the total price for all units:
; 100 EUR for 110 USD total
Assets:EUR 100 EUR @@ 110 USD
Per-unit price is computed as: 110 / 100 = 1.10 USD
Weight Calculation¶
Price Without Cost¶
When only a price annotation is present (no cost spec), the price determines the posting weight:
2024-01-15 * "Currency exchange"
Assets:EUR 100 EUR @ 1.10 USD ; Weight: 100 × 1.10 = 110 USD
Assets:USD -110 USD ; Weight: -110 USD
; Transaction balances in USD
Price With Cost¶
When both cost and price are present, the cost determines the weight (price is informational):
2024-01-15 * "Sell stock"
Assets:Stock -10 AAPL {150 USD} @ 185 USD
; Weight: 10 × 150 = 1500 USD (uses cost, not price)
Assets:Cash 1850 USD ; Weight: 1850 USD
Income:CapitalGains -350 USD ; Weight: -350 USD
; Balances: 1500 - 1850 + 350 = 0 ✓
The @ 185 USD records that AAPL was trading at $185 but doesn't affect balancing.
Implicit Price Generation¶
Transactions with price annotations can generate implicit price entries using the implicit_prices plugin:
plugin "beancount.plugins.implicit_prices"
2024-01-15 * "Exchange"
Assets:EUR 100 EUR @ 1.10 USD
Assets:USD
; Plugin generates:
; 2024-01-15 price EUR 1.10 USD
This populates the price database for later valuation.
Note: There is no option for this - it requires loading the beancount.plugins.implicit_prices plugin.
Currency Conversion¶
Simple Exchange¶
2024-01-15 * "Buy Euros"
Assets:EUR 1000 EUR @ 1.0875 USD
Assets:USD -1087.50 USD
With Fees¶
2024-01-15 * "Exchange with fee"
Assets:EUR 1000 EUR @ 1.09 USD
Expenses:Fees 5.00 USD
Assets:USD -1095.00 USD
Triangular Conversion¶
; EUR → GBP via USD rates
2024-01-15 * "EUR to GBP"
Assets:EUR -1000 EUR @ 1.10 USD
Assets:GBP 850 GBP @ 1.2941 USD
; EUR: 1000 × 1.10 = 1100 USD
; GBP: 850 × 1.2941 = 1100 USD
; Balances!
Price vs. Cost¶
| Aspect | Cost {...} |
Price @ |
|---|---|---|
| Purpose | Track acquisition basis | Record market rate |
| Creates lots | Yes | No |
| Affects weight | Always | Only without cost |
| For reductions | Matches existing lots | Informational only |
| Capital gains | Determines basis | Records sale price |
Combined Example¶
2024-01-15 * "Buy stock"
Assets:Stock 10 AAPL {150 USD} ; Cost: creates lot at 150
Assets:Cash -1500 USD
2024-06-15 * "Sell at profit"
Assets:Stock -10 AAPL {150 USD} @ 185 USD
; Cost {150 USD}: matches lot, weight = 1500 USD
; Price @ 185 USD: records market price (informational)
Assets:Cash 1850 USD
Income:CapitalGains -350 USD ; Gain: (185-150) × 10
Total Price Precision¶
Total price (@@) avoids rounding issues:
; Per-unit: may have rounding
Assets:Stock 7 AAPL @ 185.714285 USD
; Total: exact amount
Assets:Stock 7 AAPL @@ 1300 USD
; Per-unit computed: 1300 / 7 = 185.7142857...
Price Direction¶
Prices are always "base @ quote":
; 1 EUR = 1.10 USD
Assets:EUR 100 EUR @ 1.10 USD
; 1 USD = 0.91 EUR (inverse)
Assets:USD 100 USD @ 0.91 EUR
Both express the same exchange rate from different directions.
Examples¶
Stock Sale with Gain¶
2024-06-15 * "Sell Apple stock"
Assets:Brokerage -50 AAPL {150.00 USD} @ 195.50 USD
Assets:Cash 9775.00 USD
Income:CapitalGains:Long
Currency for Travel¶
2024-03-01 * "Get travel money"
Assets:Cash:EUR 500 EUR @ 1.08 USD
Assets:Checking -540 USD
Crypto Trading¶
2024-01-15 * "Buy Bitcoin"
Assets:Crypto:BTC 0.5 BTC @ 42000 USD
Assets:Checking -21000 USD
2024-06-15 * "Sell Bitcoin"
Assets:Crypto:BTC -0.5 BTC {42000 USD} @ 65000 USD
Assets:Checking 32500 USD
Income:CapitalGains:Crypto
Multi-Currency Portfolio¶
2024-01-15 * "International purchase"
Assets:Brokerage:EU 100 BMW.DE {85.50 EUR}
Assets:Cash:EUR -8550 EUR @ 1.09 USD
; BMW cost: 8550 EUR
; EUR weight: 8550 × 1.09 = 9319.50 USD equivalent
Price in Balance Assertions¶
Balance assertions don't use prices—they check units:
; Checks that you have 100 EUR, regardless of USD value
2024-01-15 balance Assets:EUR 100 EUR
; To check USD equivalent, use reporting tools, not balance
Validation¶
Price annotations have minimal validation: - Amount must be valid - Currency must be valid
The price value itself is not validated against market data.
Implementation Notes¶
- Parse
@as per-unit,@@as total - Compute weight based on presence of cost
- Optionally generate implicit price entries
- Store price for market value calculations
- Price doesn't create or modify lots