[BUG] Derived currencies (e.g. GBp) fail indirect conversion via base currency
Bug Description
In ExchangeRateDataService, the methods toCurrency(), toCurrencyAtDate(), and getExchangeRates() do not normalize derived currencies (GBp, ILA, ZAc) before constructing lookup symbols or database queries. When the system attempts indirect conversion via the base currency (USD), it constructs symbols like USDGBp instead of USDGBP. These symbols do not exist in the database, causing lookups to fail, exchange rates to be undefined, and ultimately triggering cascading big.js crashes in PortfolioCalculator.
Impact
- Every portfolio calculation involving a derived currency activity fails
- The error log is flooded with messages like:
No exchange rate has been found for GBpEUR at 2026-05-01. toCurrencyAtDate()returnsundefinedfor derived currency pairs, causingnew Big(undefined)to crash- Affects any user with GBP-denominated stocks (GBp is the standard for UK equities) or ZAR/ILS assets
loadCurrencies()stores exchange rates under the wrong key (e.g.USDGBpinstead ofUSDGBP), polluting theexchangeRatesmap and corrupting theMarketDatatable with thousands of invalid rows
Root Cause
Ghostfolio defines derived currencies in libs/common/src/lib/config.ts:
export const DERIVED_CURRENCIES = [
{ currency: 'GBp', factor: 100, rootCurrency: 'GBP' },
{ currency: 'ILA', factor: 100, rootCurrency: 'ILS' },
{ currency: 'ZAc', factor: 100, rootCurrency: 'ZAR' }
];The derivedCurrencyFactors map correctly handles direct pairs (e.g. GBp -> GBP = 1/100), but the fallback indirect conversion path in toCurrencyAtDate() uses the raw aFromCurrency and aToCurrency values to construct database query symbols like USD{currency} and {currency}USD, bypassing the derived currency logic entirely. The same issue exists in toCurrency() for the exchangeRates dictionary lookup, and in getExchangeRates() for range-based market data queries.
Steps to Reproduce
- Have a base currency of
EURand an activity with unit price inGBp - The portfolio calculator attempts to convert
GBptoEUR toCurrencyAtDate()finds no directGBpEURpair- It attempts indirect conversion by querying
USDGBpandUSDEUR - The
USDGBpquery fails, the factor becomesundefined, crashing the calculator
Expected Behavior
ExchangeRateDataService should normalize derived currencies to their root currency (e.g., GBp to GBP) before performing indirect conversions or querying the database, and then apply the factor (e.g., / 100) to the final result.
Additional Context
Fix applied in PR #6866:
- Updated
toCurrency(),toCurrencyAtDate(), andgetExchangeRates()inapps/api/src/services/exchange-rate-data/exchange-rate-data.service.tsto normalize derived currencies torootCurrencybefore any dictionary lookups or DB queries, applying the scaling factor to the final value
Database cleanup required if affected:
DELETE FROM "MarketData" WHERE symbol IN ('USDGBp', 'USDILA', 'USDZAc');Verified on a production instance with ~46k activities: no more GBpEUR errors after fix. Deleted 2049 pre-existing corrupt rows from MarketData.
Source: ghostfolio/ghostfolio