ФИНАНСОВАЯ ОБЛАСТЬP0L3

Валютная конвертация: контракт курса и воспроизводимый расчёт

Как закрепить источник, направление котировки, дату действия и версию курса, чтобы повторить финансовый пересчёт.

Справочный материал. Контракты данных, архитектурные решения и ограничения применения. Источники приведены в материалах, где используются внешние основания.

Задача и границы#

Валютная конвертация отвечает на вопрос, как исходная сумма превратилась в сумму другой валюты. Для аудита и повторного расчёта нужно восстановить не только число курса, но и источник, назначение, валютную пару, временные срезы, версию и правило округления.

Курс сделки, справочная котировка и курс, используемый для конкретного отчёта, могут различаться. Выбор зависит от деловой цели и утверждённой политики. Эта статья описывает инженерную модель, а не учётное правило организации. Примеры синтетические. Статус knowledge не означает личный опыт автора или реализацию в лаборатории.

IAS 21 рассматривает учёт операций в иностранной валюте и перевод финансовой отчётности. Функциональная валюта связана с основной экономической средой организации. Валюта отчёта в DWH сама по себе не определяет функциональную валюту. Для выбора курса нужно установить конкретное требование применимой редакции стандарта и утверждённой политики. Модель хранения ниже является предложением для DWH, а не требованием IAS 21.

Зерно, валюты и время#

Зерно входа в примере - версия строки проводки: система-источник, юридическое лицо, книга учёта, документ и номер строки. Исходную сумму хранят вместе с валютой и масштабом. Для иллюстрации дебет положителен, кредит отрицателен; положительный коэффициент сохраняет знак. Нельзя складывать суммы разных валют до согласованной конвертации.

Зерно результата можно определить как версия строки, целевая валюта, назначение расчёта и версия запуска. Исходная и конвертированная суммы остаются отдельными показателями. Если источник уже поставляет сумму в валюте книги учёта, её хранят отдельно: расчёт DWH не должен молча заменять значение источника.

Временные атрибуты отвечают на разные вопросы:

  • business_date - дата финансового события и его периода;
  • rate_date - дата курса, выбранная по политике;
  • published_at - время публикации курса провайдером;
  • observed_at - когда конкретная версия стала доступна EDWH;
  • loaded_at - когда запись загрузили в хранилище.

Дата учётного события не обязана совпадать с датой курса. Для timestamp задают часовой пояс, для дат - календарь провайдера. Правило выбора курса должно быть явным.

Контракт курса и происхождение#

Поле Смысл
provider, series_id, rate_type Провайдер, временной ряд и назначение, например справочный или договорный курс
base_currency, quote_currency Валюты, задающие направление котировки
base_units, quote_amount Число основных единиц базовой валюты и соответствующая сумма котировки
rate_date, valid_from, valid_to Дата наблюдения и заданный политикой интервал применимости
published_at, observed_at, revision_id Публикация, доступность в EDWH и версия
artifact_id, policy_version Сохранённый файл источника и версия правила выбора

Полуоткрытый интервал удобен для временной применимости: начало включается, конец исключается. Для дневного ряда правило переноса курса через нерабочий день является отдельным правилом потребителя, а не следствием наличия даты в файле. Бизнес-ключ наблюдения включает провайдера, ряд, тип, пару валют и дату. Исправление создаёт новую версию, не уничтожая предыдущую.

Европейский центральный банк публикует справочные курсы с EUR в качестве базовой валюты для информационных целей и прямо не рекомендует использовать их для транзакций. Это пример семантики конкретного провайдера, а не рекомендация выбирать курс ЕЦБ для любой учётной задачи. Доступный официальный курс может не подходить для договорной операции или регуляторного отчёта.

Для воспроизведения сохраняют полученный файл, его контрольную сумму и метаданные загрузки. Контрольная сумма помогает заметить изменение файла, но сама по себе не подтверждает надёжность провайдера. Ссылка на актуальную страницу не заменяет сохранённую версию данных.

Направление котировки и расчёт#

Пусть провайдер указывает q единиц валюты B за u основных единиц валюты A. Нормализованный курс r = q / u имеет единицу B/A. Тогда сумма в B равна amount_A * r, а обратная конвертация требует деления на r. Код пары без единиц измерения не устраняет неоднозначность.

Синтетический пример: за 100 USD получено 92 EUR. Следовательно, r = 0.92 EUR/USD; 250 USD дают 230 EUR, а конвертация 230 EUR обратно даёт 250 USD до округления. Если ошибочно прочитать 92 как курс за один доллар, результат вырастет в 100 раз. Сначала сумму исходной валюты переводят из минимальных единиц в основные по сохранённому масштабу, затем применяют масштаб целевой валюты.

Для кросс-курса через валюту C, если r_A имеет единицу A/C, а r_B - B/C, то курс B/A равен r_B / r_A. Например, синтетические курсы 1.25 USD/EUR и 0.90 GBP/EUR дают 0.72 GBP/USD. У обеих котировок должны быть согласованы провайдер, тип и временной срез. Смешение дат создаёт другой показатель. Сохраняют версии обоих курсов и путь расчёта. Кросс-курс не обязательно совпадает с исполнимой рыночной котировкой.

Рабочая точность и округление разделяются. Правила округления и зерно разобраны в статье о денежной точности. Обратная конвертация уже округлённой суммы может не восстановить исходное значение.

Версия курса и воспроизводимость#

Один из вариантов последовательности расчёта:

Зафиксировать версии строк-источников, политику и cutoff доступности.
Для каждой строки определить целевую валюту и дату курса по политике.
Выбрать версии курсов, известные на заданный cutoff.
Разрешить версии одного наблюдения согласно контракту провайдера.
Требовать ровно одну применимую котировку или разрешённый путь.
Проверить единицы, знак, конечность значения и допустимый возраст курса.
Выполнить конвертацию без преждевременного округления.
Сохранить результат, сумму источника, версии курса и правило округления.
Проверить полноту строк и расчётные результаты.

Для одинаковых валют политика может определять тождественную конвертацию с коэффициентом 1 и явной причиной, без запроса к провайдеру. Если курс отсутствует, расчёт должен завершиться отказом либо получить статус неполного. Подстановка нуля недопустима. Перенос последнего доступного курса разрешён только утверждённым правилом с ограничением возраста и отдельным признаком. Пересекающиеся интервалы и несколько подходящих рядов разрешают до соединения с фактами: обычный JOIN может размножить строки и суммы.

Снимок «как было рассчитано» использует зафиксированные входы и версии курсов. Пересчёт с учётом исправлений создаёт новый запуск и сохраняет прошлый результат. Даже при одной дате курса позднее исправление меняет доступный набор версий. Общая модель временных срезов описана в статье об истории и времени.

Альтернативы - конвертировать при каждом запросе или сохранять результаты запусков. Первый способ сокращает производные записи, но требует закрепить версии для старого отчёта. Второй упрощает аудит, но увеличивает объём хранения и ответственность за пересчёты. В обоих случаях нужна трассировка результата и нельзя выбирать «самый новый курс» без временного cutoff.

Сверки и крайние случаи#

Проверяют уникальность зерна результата, количество входных и выходных строк, наличие кодов валют и положительность и конечность применённого коэффициента. Каждая сумма должна ссылаться на исходную строку и использованные версии. Итоги сверяют по юридическому лицу, книге учёта, валюте и запуску. Баланс документа проверяют в исходной валюте; баланс после конвертации зависит также от зерна округления и политики разниц. Нельзя автоматически объявлять расхождение курсовой разницей.

До приёмки задают ожидаемое поведение для прямой и обратной котировки, курса за 100 единиц, отрицательной суммы, одинаковых валют, отсутствующего курса в выходной день, поздней корректировки и двух одновременно применимых курсов. Для кросс-курса отдельно проверяют согласованность версий компонентов. Повторный запуск с теми же входами и политиками должен дать тот же результат. Новый запуск с исправлением должен объяснять разницу изменившимися входами.

Эта модель не рассматривает отсутствие обменяемости, ограничения рынка, специальные договорные курсы или полный перевод финансовой отчётности. Для них нужны отдельные правила и оценка применимых требований. Умножение каждой строки на один курс не является универсальным алгоритмом IAS 21.

Связанные темы и вопросы#

Зерно проводки раскрыто в статье о финансовых событиях, полнота набора - в материале о сверках. Правила точности и временных срезов разобраны в связанных статьях выше. Эта статья не заменяет их общим алгоритмом.

  1. Какие единицы котировки нужно сохранять, чтобы исключить ошибку направления и масштаба?
  2. Как воспроизвести отчёт, если провайдер исправил курс за ту же дату?
  3. Что делать, если подходящего курса нет, а последняя котировка слишком старая?

Источники и документация

  1. European Central Bank - Euro foreign exchange reference rates ↗
  2. IFRS Foundation - IAS 21 The Effects of Changes in Foreign Exchange Rates ↗
  3. Python 3.14 - decimal arithmetic ↗

Источники нужны для самостоятельной проверки. Примеры и интерпретации на сайте могут быть учебными упрощениями.