C++ for Quants
  • Home
  • News
  • Contact
  • About
Category:

Risk

what is xva
Credit RiskRisk

What is X-Value Adjustment (XVA)?

by Clement D. November 16, 2025

What’s XVA? In modern derivative pricing, that question sits at the heart of almost every trading, risk, and regulatory discussion. XVA, short for X-Value Adjustments, refers to the suite of valuation corrections applied on top of a risk-neutral price to reflect credit risk, funding costs, collateral effects, and regulatory capital requirements. After the 2008 financial crisis, these adjustments evolved from a theoretical curiosity to a cornerstone of real-world derivative valuation.

Banks today do not quote the “clean” price of a swap or option alone; they quote an XVA-adjusted price. Whether the risk comes from counterparty default (CVA), a bank’s own credit (DVA), collateral remuneration (COLVA), the cost of funding uncollateralized trades (FVA), regulatory capital (KVA), or initial margin requirements (MVA), XVA brings all these effects together under a consistent mathematical and computational framework.

1.What is XVA? The XVA Family

XVA is a collective term for the suite of valuation adjustments applied to the theoretical, risk-neutral price of a derivative to reflect real-world constraints such as credit risk, funding costs, collateralization, and regulatory capital. In practice, the price a bank shows to a client is not the pure model price but the XVA-adjusted price, which embeds all these effects into a unified framework.

Modern XVA desks typically decompose the total adjustment into several components, each capturing a specific economic cost or risk. Together, they form the XVA family:

CVA – Credit Value Adjustment

CVA is the expected loss due to counterparty default. It accounts for the possibility that a counterparty may fail while the exposure is positive. Mathematically, it is the discounted expectation of exposure × loss-given-default × default probability. CVA became a regulatory requirement under Basel III and is the most widely known XVA component.

DVA – Debit Value Adjustment

DVA mirrors CVA but reflects the institution’s own default risk. If the bank itself defaults while the exposure is negative, this creates a gain from the perspective of the shareholder. While conceptually symmetric to CVA, DVA cannot usually be monetized, and its inclusion depends on accounting standards.

FVA – Funding Value Adjustment

FVA measures the cost of funding uncollateralized or partially collateralized positions.

It arises from asymmetric borrowing and lending rates: funding a derivative generally requires borrowing at a spread above the risk-free rate, and this spread becomes part of the adjusted price. FVA is highly institution-specific, sensitive to treasury curves and liquidity policies.

COLVA – Collateral Value Adjustment

COLVA captures the economic effect of posting or receiving collateral under a Credit Support Annex (CSA). It reflects the remuneration of the collateral account and the mechanics of discounting under different collateral currencies.

MVA – Margin Value Adjustment

MVA represents the cost of posting initial margin, particularly relevant for centrally cleared derivatives and uncleared margin rules. Since initial margin is locked up and earns little, MVA quantifies the funding drag associated with this constraint.

[math]
\large
\text{MVA} = -\int_0^T \mathbb{E}[\text{IM}(t)] , (f(t) – r(t)), dt.
[/math]

KVA – Capital Value Adjustment

KVA measures the cost of regulatory capital required to support the trade over its lifetime. Because capital is not free, banks incorporate a charge to account for the expected cost of holding capital against credit, market, and counterparty risk.

A commonly used representation of KVA is the discounted cost of holding regulatory capital K(t)K(t)K(t) over the life of the trade, multiplied by the bank’s hurdle rate hhh (the required return on capital):

[math]
\large
\text{KVA} = -\int_0^T D(t), h, K(t), dt
[/math]

where:

  • K(t) is the projected regulatory capital requirement at time t (e.g., CVA capital, market risk capital, counterparty credit risk capital),
  • h is the hurdle rate (often 8–12% depending on institution),
  • D(t) is the discount factor,
  • T is the maturity of the trade or portfolio.

2.The Mathematics of XVA

Mathematically, XVA extends the classical risk-neutral valuation framework by adding credit, funding, collateral, and capital effects directly into the pricing equation. The total adjusted value of a derivative is typically expressed as:

[math]
V_{\text{XVA}} = V_0 + \text{CVA} + \text{DVA} + \text{FVA} + \text{MVA} + \text{KVA} + \cdots
[/math]

where V0​ is the clean, risk-neutral price. Each adjustment is computed as an expectation under a measure consistent with the institution’s funding and collateral assumptions. CVA, for example, is the expected discounted loss from counterparty default.

Because these adjustments are interdependent, the pricing problem is no longer a simple additive correction to the clean value but a genuinely nonlinear one. Funding costs depend on expected exposures, exposures depend on default and collateral dynamics, and capital charges feed back through both. The full XVA calculation therefore takes the form of a fixed-point problem in which the adjusted value appears inside its own expectation. In practice, modern XVA desks solve this system using large-scale Monte Carlo simulations with backward induction, ensuring that all components—credit, funding, collateral, and capital—are computed consistently under the same modelling assumptions. This unified approach captures the true economic cost of trading and forms the mathematical backbone of XVA analytics in industry.

3.Calculate xVA in C++

To make the discussion concrete, we can wrap a simple XVA engine into a small, header-only C++ “library” that you can drop into an existing pricing codebase. The idea is to assume that exposure profiles and curves are already computed elsewhere (e.g. via a Monte Carlo engine) and focus on turning those into CVA, DVA, FVA, and KVA numbers along a time grid.

Below is a minimal example. It is not production-grade, but it shows the structure of a clean API that you can extend with your own models and data sources.

// xva.hpp
#pragma once
#include <vector>
#include <functional>
#include <numeric>

namespace xva {

struct Curve {
    // Discount factor P(0, t)
    std::function<double(double)> df;
};

struct SurvivalCurve {
    // Survival probability S(0, t)
    std::function<double(double)> surv;
};

struct XVAInputs {
    double V0;  // clean (risk-neutral) price

    Curve discount;
    SurvivalCurve counterpartySurv;
    SurvivalCurve firmSurv;

    std::vector<double> timeGrid;              // t_0, ..., t_N
    std::vector<double> expectedPositiveEE;    // EPE(t_i)
    std::vector<double> expectedNegativeEE;    // ENE(t_i)

    double lgdCounterparty; // 1 - recovery_C
    double lgdFirm;         // 1 - recovery_F
    double fundingSpread;   // flat funding spread (annualised)
    double capitalCharge;   // flat KVA multiplier (for illustration)
};

struct XVAResult {
    double V0;
    double cva;
    double dva;
    double fva;
    double kva;

    double total() const {
        return V0 + cva + dva + fva + kva;
    }
};

// Helper: simple forward finite difference for default density
inline double defaultDensity(const SurvivalCurve& S, double t0, double t1) {
    double s0 = S.surv(t0);
    double s1 = S.surv(t1);
    if (s0 <= 0.0) return 0.0;
    return (s0 - s1); // ΔQ ≈ S(t0) - S(t1)
}

inline XVAResult computeXVA(const XVAInputs& in) {
    const auto& t = in.timeGrid;
    const auto& EPE = in.expectedPositiveEE;
    const auto& ENE = in.expectedNegativeEE;

    double cva = 0.0;
    double dva = 0.0;
    double fva = 0.0;
    double kva = 0.0;

    std::size_t n = t.size();
    if (n < 2 || EPE.size() != n || ENE.size() != n)
        return {in.V0, 0.0, 0.0, 0.0, 0.0};

    for (std::size_t i = 0; i + 1 < n; ++i) {
        double t0 = t[i];
        double t1 = t[i + 1];

        double dt = t1 - t0;
        double dfMid = in.discount.df(0.5 * (t0 + t1));

        double dQcp = defaultDensity(in.counterpartySurv, t0, t1);
        double dQfm = defaultDensity(in.firmSurv,         t0, t1);

        double EPEmid = 0.5 * (EPE[i] + EPE[i+1]);
        double ENEmid = 0.5 * (ENE[i] + ENE[i+1]);

        // Simplified discretised formulas:
        cva += dfMid * in.lgdCounterparty * EPEmid * dQcp;
        dva -= dfMid * in.lgdFirm         * ENEmid * dQfm;

        // FVA and KVA: toy versions using EPE as proxy for funding/capital.
        fva -= dfMid * in.fundingSpread * EPEmid * dt;
        kva -= dfMid * in.capitalCharge * EPEmid * dt;
    }

    return {in.V0, cva, dva, fva, kva};
}

} // namespace xva

An example of usage:

#include "xva.hpp"
#include <cmath>
#include <iostream>

int main() {
    using namespace xva;

    XVAInputs in;
    in.V0 = 1.0; // clean price

    // Flat 2% discount curve
    in.discount.df = [](double t) {
        double r = 0.02;
        return std::exp(-r * t);
    };

    // Simple exponential survival with constant intensities
    double lambdaC = 0.01; // counterparty hazard
    double lambdaF = 0.005; // firm hazard

    in.counterpartySurv.surv = [lambdaC](double t) {
        return std::exp(-lambdaC * t);
    };
    in.firmSurv.surv = [lambdaF](double t) {
        return std::exp(-lambdaF * t);
    };

    // Time grid and toy exposure profiles
    int N = 10;
    in.timeGrid.resize(N + 1);
    in.expectedPositiveEE.resize(N + 1);
    in.expectedNegativeEE.resize(N + 1);

    for (int i = 0; i <= N; ++i) {
        double t = 0.5 * i; // every 6 months
        in.timeGrid[i] = t;

        // Toy exposures: decaying positive, small negative
        in.expectedPositiveEE[i] = std::max(0.0, 1.0 * std::exp(-0.1 * t));
        in.expectedNegativeEE[i] = -0.2 * std::exp(-0.1 * t);
    }

    in.lgdCounterparty = 0.6;
    in.lgdFirm         = 0.6;
    in.fundingSpread   = 0.01;
    in.capitalCharge   = 0.005;

    XVAResult res = computeXVA(in);

    std::cout << "V0  = " << res.V0  << "\n"
              << "CVA = " << res.cva << "\n"
              << "DVA = " << res.dva << "\n"
              << "FVA = " << res.fva << "\n"
              << "KVA = " << res.kva << "\n"
              << "V_XVA = " << res.total() << "\n";

    return 0;
}

This gives you:

  • A single header (xva.hpp) you can drop into your project.
  • A clean XVAInputs → XVAResult interface.
  • A place to plug in your own discount curves, survival curves, and exposure profiles from a more sophisticated engine.

You can then grow this skeleton (multi-curve setup, CSA terms, stochastic LGD, wrong-way risk, etc.) while keeping the same plug-and-play interface.

4.Conclusion

XVA has transformed derivative pricing from a clean, risk-neutral exercise into a fully integrated measure of economic value that accounts for credit, funding, collateral, and capital effects. The mathematical framework shows that these adjustments are not isolated add-ons but components of a coupled, nonlinear valuation problem. In practice, solving this system requires consistent modelling assumptions, carefully constructed exposure profiles, and scalable numerical methods.

The C++ snippet provided in the previous section illustrates how these ideas translate into a concrete, plug-and-play engine. Although simplified, it captures the essential workflow used on modern XVA desks: compute discounted exposures, combine them with survival probabilities and cost curves, and aggregate the resulting adjustments into a unified valuation.

As models evolve and regulatory requirements tighten, XVA will continue to shape how financial institutions assess the true cost of trading. A solid understanding of its mathematical foundations and computational techniques is therefore indispensable for quants and risk engineers looking to build accurate, scalable, and future-proof pricing systems.

November 16, 2025 0 comments
What is dv01
Risk

What Is DV01? An Implementation in C++

by Clement D. July 26, 2025

What is DV01? In fixed income markets, DV01 (Dollar Value of 01) is one of the most important risk measures every quant, trader, and risk manager needs to understand. DV01 tells you how much the price of a bond, swap, or portfolio changes when interest rates shift by just one basis point (0.01%). This tiny movement in yield can translate into thousands or even millions of dollars in profit or loss for large portfolios.

In other words, DV01 measures interest rate sensitivity in dollar terms. If you’ve ever wondered “What is DV01 in bonds?”, think of it as the financial system’s ruler for measuring how prices react to micro-changes in rates.

For quants, DV01 is the foundation for hedging strategies, scenario analysis, and stress testing. For developers, it’s a key calculation baked into trading systems and risk engines. In this article, we’ll explore what DV01 really is, explain the math behind it, and provide a clean, modern C++ implementation to compute DV01 for single bonds and entire portfolios.

What is DV01?

DV01, short for Dollar Value of 01, measures the dollar change in a bond’s price when its yield changes by one basis point (0.01%). It’s derived directly from the bond’s price–yield relationship: when yields rise, bond prices fall, and DV01 quantifies that sensitivity. Mathematically, DV01 is the negative derivative of price with respect to yield, scaled by 1 basis point:

[math] \Large dv01 = – \frac{dP}{dy} \times 0.0001 [/math]

Where:

  • P = price of the bond (in dollars).
  • y = yield to maturity (as a decimal, e.g. 0.05 for 5%).
  • [math] \frac{dP}{dy} [/math] = the rate of change of the bond price with respect to yield (the slope of the price–yield curve).
  • 0.0001 = one basis point expressed as a decimal (1bp = 0.01% = 0.0001).

Example:
A 5-year bond priced at $102 with a modified duration of 4.5 has:

[math] DV01 = 4.5 \times 102 \times 0.0001 = 0.0459 [/math]

This means that if yields go up by just 1bp, the bond’s price will drop by 4.59 cents.

A C++ Implementation

Here is an implementation of a dv01 calcualtion in C++:

#include <iostream>
#include <vector>
#include <cmath>

// --- Bond structure ---
struct Bond {
    double face;      // Face value (e.g. 100)
    double coupon;    // Annual coupon rate (as decimal, e.g. 0.05 for 5%)
    int maturity;     // Maturity in years
    double yield;     // Yield to maturity (as decimal)
};

// --- Price a plain-vanilla annual coupon bond ---
double priceBond(const Bond& bond) {
    double price = 0.0;
    for (int t = 1; t <= bond.maturity; ++t) {
        price += (bond.face * bond.coupon) / std::pow(1 + bond.yield, t);
    }
    price += bond.face / std::pow(1 + bond.yield, bond.maturity); // Add principal repayment
    return price;
}

// --- DV01 using the bump method ---
double dv01(const Bond& bond) {
    constexpr double bp = 0.0001; // One basis point
    double basePrice = priceBond(bond);

    Bond bumpedBond = bond;
    bumpedBond.yield += bp; // bump yield by 1bp

    double bumpedPrice = priceBond(bumpedBond);

    return basePrice - bumpedPrice; // DV01 in dollars
}

// --- Example usage ---
int main() {
    Bond bond = {100.0, 0.05, 5, 0.04}; // Face=100, 5% coupon, 5-year, 4% yield
    double price = priceBond(bond);
    double dv01Value = dv01(bond);

    std::cout << "Bond Price: $" << price << std::endl;
    std::cout << "DV01: $" << dv01Value << std::endl;

    return 0;
}

This simple struct holds the key attributes of a plain-vanilla fixed-rate bond:

  • Face: The amount the bond will pay back at maturity.
  • Coupon: Annual interest rate the bond pays.
  • Maturity: Number of years until final repayment.
  • Yield: Market-required return, used for discounting future cashflows.

To calculate the dv01 here we:

  • Takes the original bond price.
  • Bumps the yield by one basis point (0.0001).
  • Re-prices the bond using the bumped yield.
  • Subtracts the bumped price from the original price.

This gives the DV01 the measures how much the price changes when yields shift by 1bp: this “bump method” is exactly what traders and risk systems use.

Compile and Run

Create dv01.cpp containing the code above, as well as a CMakeLists.txt like:

cmake_minimum_required(VERSION 3.10)
project(dv01)
set(CMAKE_CXX_STANDARD 17)
add_executable(dv01 ../dv01.cpp)

Then run the compilation:

mkdir build
cd build
cmake ..
make

Then run the program:

./dv01
Bond Price: $104.452
DV01: $0.0457561

The code of the application is accessible here:

https://github.com/cppforquants/dv01

Alternative Implementations

When moving from educational examples to real-world analytics, most teams don’t maintain their own pricing code: they turn to professional libraries. In C++, the dominant choice is QuantLib, an open-source framework used by banks, hedge funds, and trading desks worldwide. QuantLib offers several advantages for calculating bond price sensitivity:

  • It handles all the details you would otherwise code by hand, including calendars, settlement dates, and day count conventions.
  • It includes a wide range of pricing engines, so the same approach works for fixed-rate bonds, floaters, swaps, and even more complex instruments.
  • It allows you to shift the yield curve directly and reprice instantly, so bumping rates for sensitivity tests is just a matter of swapping in a different term structure.
  • It ensures consistency with market standards, which is critical if your numbers need to match the desk’s systems.

For a teaching example, writing the pricing loop yourself is helpful… But for production, using QuantLib means fewer bugs, faster development, and calculations that match what traders and risk managers expect.

#include <ql/quantlib.hpp>
#include <iostream>

using namespace QuantLib;

int main() {
    try {
        // 1. Set the evaluation date
        Calendar calendar = TARGET();
        Date today(26, July, 2025);
        Settings::instance().evaluationDate() = today;

        // 2. Define bond parameters
        Date maturity(26, July, 2030);
        Real faceAmount = 100.0;
        Rate couponRate = 0.05; // 5% annual coupon

        // 3. Build the bond schedule
        Schedule schedule(today, maturity, Period(Annual), calendar,
                          Unadjusted, Unadjusted, DateGeneration::Forward, false);

        // 4. Create the fixed-rate bond
        FixedRateBond bond(3, faceAmount, schedule,
                           std::vector<Rate>{couponRate},
                           ActualActual(ActualActual::ISDA));

        // 5. Build the flat yield curve at 4%
        Handle<YieldTermStructure> curve(
            boost::make_shared<FlatForward>(today, 0.04, ActualActual(ActualActual::ISDA)));

        // 6. Attach a discounting engine
        bond.setPricingEngine(boost::make_shared<DiscountingBondEngine>(curve));

        // 7. Compute base price
        Real basePrice = bond.cleanPrice();

        // 8. Bump the yield curve by 1bp (0.01%) and reprice
        Handle<YieldTermStructure> bumpedCurve(
            boost::make_shared<FlatForward>(today, 0.0401, ActualActual(ActualActual::ISDA)));
        bond.setPricingEngine(boost::make_shared<DiscountingBondEngine>(bumpedCurve));

        Real bumpedPrice = bond.cleanPrice();

        // 9. Compute and print sensitivity
        Real priceChange = basePrice - bumpedPrice;

        std::cout << "Base Price: " << basePrice << std::endl;
        std::cout << "Price Change for 1bp shift: " << priceChange << std::endl;
    }
    catch (std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return 1;
    }

    return 0;
}

To run the example, install QuantLib (for example, via your system package manager or by building from source) and compile with a standard C++17 or later compiler:

g++ -std=c++17 -I/usr/include/ql -lQuantLib sensitivity_example.cpp -o sensitivity_example
./sensitivity_example

This produces the base bond price and the change in price after a 1bp shift in the yield curve. From there, you can expand the approach:

  • Price a portfolio of bonds by looping through multiple instruments and summing their sensitivities.
  • Swap in a different term structure (e.g., a real yield curve from market data) to see how results change under new scenarios.
  • Experiment with different bond types like floaters or callable bonds by just changing the instrument class and pricing engine.

These extensions show how the same core idea can scale from a single-bond demo into a risk engine component that handles thousands of securities and multiple yield environments.

July 26, 2025 0 comments

@2025 - All Right Reserved.


Back To Top
  • Home
  • News
  • Contact
  • About