Понимание C++20 <chrono> в контексте количественных финансов

Понимание C++20 <chrono> в контексте количественных финансов

20 февраля 2023 г.

Цель статьи — продемонстрировать преимущества новейших функций современного C++ и std::chrono в различных областях, включая приложения математического моделирования для количественных финансов.

Введение

Управление временем – обычная задача в различных современных отраслях, таких как технологии, медицина и финансы. Это особенно важно для количественных финансов, где время может быть важным фактором, например, с ценными бумагами с фиксированным доходом. В качестве иллюстрации можно рассмотреть кредитные продукты, такие как облигации и CDS (дефолтный своп).

В настоящее время CDS стали взаимозаменяемыми с введением стандартных купонов. Практическое значение этого заключается в том, что должно быть легко заменить один стандартный контракт CDS другим, а стандартные даты платежей CDS были введены относительно 20 марта, июня, сентября и декабря каждого года.

Периодические платежи осуществляются ежеквартально, а суммы платежей рассчитываются в соответствии с соглашением о подсчете ACT/360 дней при оценке премиальной ветви CDS. Чтобы проиллюстрировать это, предположим, что C — купон, а t_0, t_1, ..., t_n — даты начисления (окончания).

Согласно ACT/360, суммы денежных потоков можно рассчитать следующим образом:

C_i = C * (t_i - t_{i - 1}) / 360

Этот простой пример демонстрирует решающую роль вычислений даты и времени для современных приложений финансового ценообразования.

Каноническим языком программирования для создания библиотек ценообразования является C++, в котором есть std::chrono для работы со временем, начиная с C++11, а начиная с C++20 он имеет новые функции, которые могут помочь специалистам по количественному анализу использовать мощь C++ в своей повседневной работе.

Этими функциями могут быть:

  • новые часы: utc_clock, tai_clock, gps_clock
  • год_месяц_день
  • оператор sys_days
  • часы

Цель этой статьи — продемонстрировать на простых фиктивных примерах, как работать с некоторыми из этих новых функций. Код был протестирован в MS Visual Studio 2022, но ожидается, что его можно будет скомпилировать с помощью GCC и Clang, как только будут реализованы соответствующие функции библиотеки.

Перейдем к техническим деталям.

Некоторые полезные функции std::chrono

В первой части статьи мы рассмотрим использование utc_clock, year_month_day и operator sys_days. Затем мы создадим собственный класс часов, имитирующий те, что в std::chrono. Наконец, мы создадим класс часов, преобразуемый в system_clock, который является одним из наиболее широко используемых классов часов.

Чтобы начать использовать Chrono, в код необходимо включить заголовок <chrono>. Далее мы введем псевдоним для пространства имен std::chrono, который будем использовать в следующих примерах:

namespace chr = std::chrono;

С++ 20 имеет широкий набор литералов, которые помогают определить дату. Мы можем сделать это с помощью оператора / перегрузки

using chr::March;

chr::year_month_day const cds_date = March / 20 / 2023;

или так

using namespace std::literals;

chr::year_month_day const cds_date = 2023y / 03 / 20d;

Экземпляр класса year_month_day можно легко преобразовать в system_clock::time_point с помощью нового оператора C++20 sys_days:

chr::system_clock::time_point const tp_sys = chr::sys_days(cds_date);

и привести его, например, к time_point для utc_clock:

chr::time_point<chr::utc_clock> const tp_utc = chr::clock_cast<chr::utc_clock>(tp_sys);

Очевидно, здесь мы можем использовать все механизмы из предыдущего стандарта C++, и хорошим примером здесь является duration_cast.

В следующей главе мы узнаем, как создать собственный класс часов, совместимый со всеми перечисленными выше функциями C++20 для Chrono.

Как создать собственные часы

Далее мы не будем сильно зацикливаться на финансовых деталях и создадим очень простой пример часов, которые начинаются в начале тысячелетия, то есть 1 января 2000 года. Но методы, которые мы будем использовать, полностью применимы к финансовым проблемам при работе с датами — мы просто хотим, чтобы все было как можно проще для демонстрационных целей:

#include <cstdint>
#include <chrono>

class millennium_clock final
{
public:
    using rep = std::int32_t;
    using period = std::ratio<1, 1>;
    using duration = std::chrono::duration<rep, period>;
    using time_point = std::chrono::time_point<millennium_clock>;

    static constexpr bool is_steady = false;

    static time_point now()
    {
        return time_point{ std::chrono::duration_cast<duration>(std::chrono::system_clock::now() - epoch) };
    }

private:
    static std::chrono::sys_time<duration> const epoch;
};

std::chrono::sys_time<millennium_clock::duration> const millennium_clock::epoch { std::chrono::sys_days(std::chrono::January / 1 / 2000) };

static_assert(std::chrono::is_clock_v<millennium_clock>);

Здесь мы имитируем методы, имеющиеся у нас для класса system_clock, а затем проверяем во время компиляции, что созданный нами класс полностью удовлетворяет всем требованиям к часам chrono. Также обратите внимание, что период должен быть равен std::ratio и что основными единицами измерения для него являются секунды.

Возможно, мы хотели бы иметь возможность использовать в нашем коде приведения от наших часов к, скажем, system_clock:

auto const& now_mil = millennium_clock::now();
auto const& now_sys = chr::clock_cast<chr::system_clock>(now_mil);

Для этого нам нужно сделать специализацию для класса clock_time_conversion:

namespace std::chrono {
    // specialization for std::chrono::clock_cast
    template <>
    struct clock_time_conversion<system_clock, millennium_clock>
    {
        template <typename Duration>
        sys_time<Duration> operator()(time_point<millennium_clock, Duration> const& tp) const
        {
            return sys_time<Duration>(tp.time_since_epoch() + millennium_clock::epoch);
        }
    };
}

Последним шагом является предоставление доступа к классу clock_time_conversion закрытым членам класса millennium_clock:

class millennium_clock final
{
    friend struct std::chrono::clock_time_conversion<std::chrono::system_clock, millennium_clock>;

Вот и все, теперь мы можем преобразовать time_point с помощью clock_cast!

Заключение

В статье демонстрируются некоторые передовые методы использования современного C++ при работе с <chrono> с акцентом на потенциальные приложения для разработки финансовых библиотек. Он наглядно демонстрирует, что можно писать элегантный и эффективный код на C++ и как использовать эти методы с последним стандартом C++.


Ведущий образ создан со стабильной диффузией.


Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE