Toy Metaprogramming

Metaprogramming is a broad and complicated concept. There are several thick books talking about it, for example, “C++ Template Metaprogramming: Concepts, Tools, and Techniques from Boost and Beyond”. I see it as a template programming tool that helps transfer some run-time work to compile time.

Below is a toy example of metaprogramming, a for-loop on compile time to print variable types. To see how it is interpreted on the compiler side, copy the code below to https://cppinsights.io/ and set the version to C++ 20 (It must be C++ 20 and after because the older versions use enable if instead of requires).

#include <tuple>
#include <memory>
#include <type_traits>
#include <iostream>
#include <iomanip>
 
template<typename TypeToPrint>
void printType(TypeToPrint&& t) {
    std::cout << typeid(t).name() << std::endl;
}
 
template<typename... TypesToPrint>
void printType(TypesToPrint&&... ts) {
    (printType(ts), ...);
}

// for loop base case 
template<size_t I = 0, typename... TypesToPrint>
requires (I == sizeof...(TypesToPrint))
void printTypeNew(const std::tuple<TypesToPrint...>&) {}

// for loop reduction
template<size_t I = 0, typename... TypesToPrint>
requires (I < sizeof...(TypesToPrint))
void printTypeNew(const std::tuple<TypesToPrint...>& ts) {
    printType(get<I>(ts));
    printTypeNew<I + 1, TypesToPrint...>(ts);
}
 
template <typename... Types>
struct Tuple {
    std::tuple<Types...> t;
    void printAllTypes() const {
        std::apply([](auto&&... ts){ printType(ts...); }, t);
    }
    void printAllTypesNew() const {
        printTypeNew(t);
    }
};
 
int main() {
    Tuple<int*, std::unique_ptr<float>> T2;
    T2.printAllTypes();
    std::cout << "New way" << std::endl;
    T2.printAllTypesNew();
}

Interest Rates Basic

Suppose b>a>0. Let P(a,b) be the discount factor discounting 1 dollar paid at b to time a. Let T_1, T_2, \ldots, T_{N+1} be the floating rate reset date of the floating leg in a swap. For the case of LIBOR 3M, T_i = \frac{i}{4} if we assume the day count convention is that 3 months is \frac{1}{4} of a year. Define \alpha_n = T_{n+1} - T_{n}.

Forward LIBOR Rate

Let L_n(t) be the forward LIBOR rate for period [T_n, T_{n+1}] seen at time t for t < T. Then,

    \[L_n(t,T) := \frac{1}{\alpha_n}\left(\frac{P(t,T_n)}{P(t,T_{n+1})}-1\right).\]

This definition makes sense if we seen it as:

    \[1+\alpha_nL_n(t) = \frac{1}{P(T_n, T_{n+1})} = \frac{P(t,T_n)}{P(t,T_{n+1})}.\]

Instantaneous Forward Rate

Instantaneous forward rate is the forward rate with time interval goes to infinity.

    \[\begin{aligned}f(t,T)&=\lim_{\delta\rightarrow 0}\frac{1}{\delta}\left(\frac{P(t,T)}{P(t,T+\delta)}-1\right) \\ & = -\frac{\partial \log P(t,T)}{\partial T}\end{aligned}.\]

Short Rate

Short rate r(t) is an instantaneous interest rate such that if r is deterministic

    \[P(t,T) = e^{-\int_t^Tr(s)\textnormal{d}s}.\]

Or if r is stochastic

    \[P(t,T) = \mathbf{E}\left[e^{-\int_t^Tr(s)\textnormal{d}s}|\mathcal{F}_t\right].\]

Forward Par Swap Rate

In finance, ‘par’ usually means ‘equal’. Par swap rate is the fixed rate such that the floating leg and the fixed leg of the swap make the contract has value 0. We denote S_n(t) by the forward par swap rate on tenor [T_n, T_{N+1}]. Suppose the pay frequency of the fixed leg is the same as the floating leg (otherwise redefine the below annuity as one that matches the fixed leg pay frequency). Let A_n(t) represents the present value of annuity, i.e.,

    \[A_n(t) = \sum_{k=n+1}^{N+1} \alpha_{k-1}P(t,T_k).\]

Then the present value of the fixed leg is

    \[PV_{\mbox{fix}} = S_n(t)A_n(t)\]

The present value of the floating leg is

    \[\begin{aligned}& PV_{\mbox{float}} \\ = & \sum_{k=n}^N L_k(t)\alpha_k P(t,T_{k+1}) \\ =&  \sum_{k=n}^N \frac{1}{\alpha_k}\left(\frac{P(t,T_k)}{P(t,T_{k+1})}-1\right) \alpha_k P(t,T_{k+1}) \\ =&  \sum_{k=n}^N \left(P(t,T_k) - P(t,T_{k+1})\right) \\ =& P(t,T_n) - P(t,T_{N+1}) \end{aligned}\]

By letting PV_{\mbox{fix}} = PV_{\mbox{float}}, we have that

    \[S_n(t) = \frac{P(t,T_n) - P(t,T_{N+1})}{A_n(t)}.\]

Feynman-Kac Formula

Feynman-Kac formula connects the solution to a SDE to the solution of a PDE. For example, the Black-Scholes formula is an application of Feynman-Kac.

Let X(t) satisfies the following SDE driven by standard Brownian Motion:

    \[\textnormal{d}X_u = \beta(u,X_u)\textnormal{d}u + \gamma(u,X_u\textnormal{d}W_u).\]

Let h(y) be a Borel measurable function, and t \in [0,T]. Define

    \[\begin{aligned} g(t,x)  := \mathbf{E} [&\int_t^Te^{-\int_t^TV(\tau,X_{\tau})\textnormal{d}\tau}f(r,X_r)\textnormal{d}r \\ &+e^{-\int_t^TV(\tau,X_{\tau})}h(X_T)|X_t = x ] \end{aligned}.\]

Then, g(t,x) satisfies PDE

    \[g_t + \beta g_x + \frac{1}{2}\gamma^2 g_{xx}  - Vg + f = 0.\]

Proof.

To get an idea of how this formula is proved, we start with a base case. The general case is proved by the same manner with a little more complicated calculation.

Suppose V=f=0.

Let \mathcal{F}(t) be the natural filtration associated with the standard Brownian Motion in the SDE. By the Markov property of the solution to SDE, we have \forall s \in [0,T],

    \[\mathbf{E}[h(X_T)|\mathcal{F}(s)] = \mathbf{E}[h(X_T)|\sigma(X_s)] = g(s,X_s).\]

Then, for 0<s<t<T

    \[\begin{aligned} \mathbf{E}[g(t,X_t)|\mathcal{F}(s)] & =  \mathbf{E}[ \mathbf{E}[h(X_T)| \mathcal{F}(t) ] |\mathcal{F}(s)]  \\ &=  \mathbf{E}[h(X_T)|\mathcal{F}(s)]  =  g(s,X_s)  \end{aligned}\]

Hence, g(t,X_t) is a martingale.

Then, we apply Ito’s formula to g(t,X_t). Its drift term should be zero because it is a martingale. This leads to the Feynman-Kac PDE.

    \[\begin{aligned} \textnormal{d}g(t,X_t) & = g_t\textnormal{d}t + g_x \textnormal{d} x + \frac{1}{2}g_{xx} \textnormal{d} X \textnormal{d} X \\ & = [g_t+\beta g_x + \frac{1}{2}\gamma^2 g_{xx}] \textnormal{d} t + \gamma g_x  \textnormal{d} W\end{aligned}.\]

By setting the \textnormal{d} t term equal to 0, we get the PDE:

    \[g_t+\beta g_x + \frac{1}{2}\gamma^2 g_{xx} = 0.\]

For the general case, we only need to factor out the \int_0^t integral and make the integral inside the expectation only contains \int_0^T term. We will get G(t,X_t) is a martingale with G(t,X_t) defined as:

    \[G(t,x):= e^{-\int_0^tV(\tau,X_{\tau})\textnormal{d}\tau}(g(t,x)+\int_0^t e^{-\int_t^rV (\tau,X_{\tau})\textnormal{d}\tau } f(r,X_r) \textnormal{d}r)\]

Similarly, by applying Ito’s formula to above martingale and setting the drift term equal to 0, we will get the result.