All guides

Currency conversion at totals: SELECTEDVALUE, blended rates and the SUMX you actually need

Why a converted amount is right on every row and wrong on the total, and the row-level pattern that fixes it without scanning the fact table twice.

7 min read · updated 2026-09-21

The symptom

Sales in EUR is correct for France, correct for Japan, and wrong on the total row. Or the total is fine until someone adds a month slicer. Either way the numbers on the rows do not add up to the number at the bottom, and the report loses credibility in one meeting.

Why it happens

The measure picks one exchange rate with SELECTEDVALUE (or MAX, or a lookup) and multiplies the total amount by it. On a row where one currency is in context there is one rate, so it works. On the total there are many currencies: SELECTEDVALUE returns blank or the alternate result, MAX returns the highest rate, AVERAGE returns a blended rate. None of them is the sum of the converted rows.

A second variant wraps the rate in a helper measure that returns a text message ('Select one currency') when several are in context. Any arithmetic on that measure is then a type error, and dependent measures show blank.

Correct per row, wrong at totals
Sales EUR (wrong) =
VAR Rate = SELECTEDVALUE ( FxRates[RateToEUR], 1 )
RETURN
    [Sales local] * Rate

The row-level pattern

Convert at the granularity where the rate is unique, then sum. Iterate over the distinct combinations of currency and period (not over the fact table), fetch the rate once per combination, multiply the sales of that combination, and add up.

This is also the fast version. The common alternative, ADDCOLUMNS over the whole fact table with a FILTER on the rate table inside, computes the rate lookup once per fact row and turns a 2 ms measure into a 20 s one on 10 million rows.

Sum of converted amounts
Sales EUR =
SUMX (
    SUMMARIZE ( Sales, Sales[Currency], DimDate[YearMonth] ),
    VAR Rate =
        CALCULATE (
            SELECTEDVALUE ( FxRates[RateToEUR] ),
            TREATAS ( { Sales[Currency] }, FxRates[Currency] ),
            TREATAS ( { DimDate[YearMonth] }, FxRates[YearMonth] )
        )
    RETURN
        [Sales local] * Rate
)

What the audit checks

Statically: iterators that also carry FILTER or CALCULATE over the fact, arithmetic on a measure that returns text, and measures whose return type varies by branch. The AI review specifically tests every conversion measure for the total-row case, because that is what an expert would try first.

Detected by

Checks dax-performance, dax-arithmetic-on-text-measure, dax-variant-return-type, expert-check-4 among the 363 static checks, plus the AI review for the cases static analysis cannot prove.

Next: Bidirectional relationships: when they silently corrupt totals