Understanding C++20 <chrono> in the Context of Quantitative Finance

Written by quant | Published 2023/02/19
Tech Story Tags: c++ | quantitative-research | programming | fixed-income | mathematics | mathematics-and-programming | finance | banking

TLDRThe purpose of the article is to demonstrate the benefits that the recent features of modern C++ and std::chrono have on various fields, including mathematical modeling applications. The code was tested on MS Visual Studio 2022 but is expected to be compilable with both GCC and Clang as soon as the corresponding library features are implemented.via the TL;DR App

The purpose of the article is to demonstrate the benefits that the recent features of modern C++ and std::chrono have on various fields, including mathematical modeling applications for Quantitative Finance.

Introduction

Dealing with time is a common task in various modern industries such as technology, medicine, and finance. It is especially important for Quantitative Finance where time can be an important factor, for instance with Fixed Income Securities. For illustration, we can consider credit products, such as Bonds and CDS (Credit Default Swap).

CDS are made fungible nowadays with the introduction of standard coupons. The practical meaning of this is that it should be easy to replace one standard CDS contract with another, and the standard payment CDS dates were introduced with respect to the 20th of March, June, September, and December each year.

The periodic payments are quarterly with the payment amounts computed following the ACT/360-day count convention when valuing the premium leg of the CDS. To illustrate this, suppose C is the coupon and t_0, t_1, ..., t_n are accrual (end) dates.

According to ACT/360, the amounts of cash flows can be calculated as follows:

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

This simple example demonstrates the crucial role of date and time computations for modern financial pricing applications.

The canonical programming language for building pricing libraries is C++, which has std::chrono to work with time since C++11, and since C++20 it has new features which could help quants to leverage the power of C++ more effectively in their everyday job.

Those features could be:

  • new clocks: utc_clock, tai_clock, gps_clock
  • year_month_day
  • operator sys_days
  • clock_cast

The purpose of this article is to demonstrate, with simple mock examples, how to work with some of these new features. The code was tested on MS Visual Studio 2022 but is expected to be compilable with both GCC and Clang as soon as the corresponding library features are implemented.

Let's move on to technical details.

Some useful std::chrono features

In the first part of the article, we will consider the use of utc_clock, year_month_day and operator sys_days. Then, we will create our own clock class to mimic those in std::chrono. Finally, we will make out clock class convertible to system_clock, which is one of the most widely used clock classes.

One must include <chrono> header into the code to start using Chrono. Next, we will introduce an alias for std::chrono namespace which we will use in the following examples:

namespace chr = std::chrono;

C++20 has a reach set of literals that helps to define a date. We can do this with operator / overload

using chr::March;

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

or this way

using namespace std::literals;

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

It is possible to easily convert an instance of year_month_day class into system_clock::time_point with new C++20 sys_days operator:

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

and to cast it, for example, to time_point for utc_clock:

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

Obviously, we can leverage here all the machinery from the previous C++ standard, and a good example here is duration_cast.

In the next chapter, we will learn how to create our own clock class compatible with all the above C++20 features for Chrono.

How to create your own clock

Further, we won't concentrate much on financial details and will create a very simple sample clock that starts at the beginning of the millennium, i.e. on January 1, 2000. But the techniques we will use are completely relevant for the financial problems when dealing with dates -- we just want to keep things as simple as possible for demonstration purposes:

#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>);

Here we mimic the methods we have for system_clock class and then checking on compile time that the class we created completely satisfies all requirements for chrono clocks. Please also note that the period must be std::ratio and that the basic units for it are seconds.

Potentially, we would like to be able to use casts in our code from our clock to, let's say, system_clock:

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

To do that, we need to make a specialization for clock_time_conversion class:


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);
        }
    };
}

The last step is to give the access for clock_time_conversion class to private members of millennium_clock class:

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

That's it, now we can cast a time_point using clock_cast!

Conclusion

The article demonstrates some good practices for modern C++ use when working with <chrono> with emphasis on the potential applications for financial libraries development. It clearly demonstrates that it is possible to write elegant and efficient C++ code and how to leverage these practices with the latest C++ standard.


Lead image generated with stable diffusion.


Written by quant | Credit Desk Quant @ Barclays
Published by HackerNoon on 2023/02/19