Адекватно ли считать сумму разных валют без конвертации
Что проверяют
Замечаете ли вы, что деньги это пара «сумма и валюта», а не просто число. Вопрос обычно вырастает из задачи на код: посчитать итог по списку транзакций. Следом спрашивают, по какому курсу и на какую дату конвертировать и в каком типе хранить суммы.
Как отвечать
Короткий ответ: складывать числа в разных валютах бессмысленно, 100 USD и 100 EUR не дают 200 чего-либо. Сильный ответ добавляет, что корректных вариантов два:
- итог по каждой валюте:
{"USD": 100, "EUR": 250}, без конвертации. Часто это и есть правильный ответ, например для баланса мультивалютного кошелька; - итог в валюте отчёта: каждая сумма переводится по курсу на дату операции или на дату отчёта, и это разные бизнес-требования. Курс берут из одного источника, ЦБ или ЕЦБ, и сохраняют рядом с результатом, чтобы отчёт воспроизводился.
Дальше реализация на Python:
- тип:
decimal.Decimal, а неfloat.0.1 + 0.2воfloatдаёт0.30000000000000004. Ещё вариант: целое число в минимальных единицах, копейках или центах; - округление: явное,
quantize(Decimal("0.01"), rounding=ROUND_HALF_EVEN)или по правилу бизнеса. Число знаков зависит от валюты, у иены их нет; - модель:
dataclassMoney(amount, currency), у которого__add__бросает исключение при разных валютах. Тогда ошибку ловит код, а не бухгалтер. Из готового естьpy-moneyed, для форматированияbabel; - база:
NUMERICи отдельная колонка с кодом валюты по ISO 4217.
Где спотыкаются
Отвечают «конвертируем по текущему курсу» и не спрашивают, на какую дату. Отчёт за прошлый квартал по сегодняшнему курсу при каждом запуске даёт новую цифру.
Считают во float и округляют только при выводе. На тысячах операций ошибка накапливается, и итог расходится с банковской выпиской на копейки.
Конвертируют на каждом промежуточном шаге. Каждое округление теряет точность, поэтому суммы держат в исходной валюте и переводят один раз, в конце.