Bond pricing is a fundamental skill for any quant. Whether you’re managing fixed-income portfolios or calculating risk metrics, understanding how a bond is priced is essential. In this article, we’ll walk through the core formula, explore how clean and dirty prices differ, and implement it in C++ using QuantLib. How to build a bonds pricer?
1.The Bond Pricing Formula
When you buy a bond, you’re essentially buying a stream of future cash flows: fixed coupon payments and the return of the face value at maturity. The price you pay for that stream depends on how much those future payments are worth today — in other words, their present value.
🔹 The Dirty Price: What You’re Really Paying
The dirty price (or full price) is the total value of the discounted future cash flows:
[math] \Large{\text{Dirty Price} = \sum_{i=1}^{N} \frac{C_i}{(1 + y)^{t_i}}} [/math]
Where:
- [math]C_i[/math] is the cash flow at time [math]t_i[/math] — typically the coupon, and the last cash flow includes the face value.
- [math]y[/math] is the periodic yield, i.e., the annual yield divided by the number of coupon payments per year.
- [math]t_i[/math] is the year fraction between today and the [math]i^\text{th}[/math] payment, calculated using day count conventions (like Actual/360, 30/360, etc.).
In simpler terms: money in the future is worth less than money today, and the discounting reflects that.
🔹 Accrued Interest: What You Owe the Seller
Most bonds trade between coupon dates, meaning the seller has already “earned” part of the next coupon. To make things fair, the buyer compensates them via accrued interest:
[math] \Large \text{Accrued Interest} = \text{Coupon} \times \frac{\text{Days since last payment}}{\text{Days in period}} [/math]
So if you buy the bond halfway through the coupon cycle, you’ll owe the seller half the coupon. This ensures the next payment (which you’ll receive in full) is properly split based on ownership time.
🔹 Clean Price: The Market Quote
Bond prices are typically quoted clean, without accrued interest:
[math] \Large \text{Clean Price} = \text{Dirty Price} – \text{Accrued Interest} [/math]
This keeps things tidy when quoting and trading, and lets the system calculate accrued interest automatically behind the scenes.
Together, these three equations form the backbone of bond pricing. In the next section, we’ll show how QuantLib brings them to life — no need to write your own discounting engine.
3. Setting Up the Bond in QuantLib
Now that we’ve covered the theory behind bond pricing, let’s see how to implement it using QuantLib. One of QuantLib’s biggest strengths is how it mirrors the real-world structure of financial instruments — every component reflects a real aspect of how bonds are modeled, priced, and managed in production systems.
Here’s what we need to set up a bond in QuantLib:
📅 Calendar, Date, and Schedule
Calendar
: Tells QuantLib which days are business days. This is crucial for calculating coupon dates and settlement dates correctly. We’ll useTARGET()
, a commonly used calendar for euro-denominated instruments.Date
: QuantLib’s custom date class used to define evaluation, issue, maturity, and coupon dates.Schedule
: Automatically generates all coupon dates between the start and maturity dates. You specify the frequency (e.g., semiannual), the calendar, and how to adjust dates if they fall on weekends or holidays.
In other words: this trio defines when things happen.
💵 FixedRateBond
- This is the actual bond object.
- It takes in:
- Settlement days (e.g., T+2),
- Face value (e.g., 1000),
- The coupon schedule,
- The fixed coupon rate(s),
- Day count convention (e.g., Actual/360),
- Date adjustment rules.
- Once constructed, it contains all the future cash flows, knows when they occur, and how much they are.
Think of FixedRateBond
as your contractual definition of the bond.
📈 YieldTermStructure
- This represents the discount curve.
- You can define it using:
- A flat yield (e.g., 4.5% across all maturities),
- A bootstrap from market instruments (swaps, deposits, etc.),
- Or even a custom curve from CSV or historical data.
- QuantLib uses this curve to discount each cash flow to present value.
This is your interest rate environment — essential for pricing.
🧠 DiscountingBondEngine
- This is the pricing engine: the part of QuantLib that ties it all together.
- Once you set the bond’s pricing engine to a
DiscountingBondEngine
, it knows how to price it using the curve. - It computes:
- The dirty price,
- The clean price,
- The accrued interest,
- And other risk measures like duration and convexity.
You can think of it as the calculator that applies all the math we just discussed.
4. Implementation
Let’s implement:
#include <ql/quantlib.hpp>
#include <iostream>
#include <iomanip>
using namespace QuantLib;
int main() {
// Set the evaluation date
Date today(24, June, 2025);
Settings::instance().evaluationDate() = today;
// Bond parameters
Real faceValue = 1000.0;
Rate couponRate = 0.05; // 5%
Date maturity(24, June, 2030);
Frequency frequency = Semiannual;
Integer settlementDays = 2;
DayCounter dayCounter = Actual360();
BusinessDayConvention convention = Unadjusted;
Calendar calendar = TARGET();
// Build the schedule of coupon payments
Schedule schedule(today, maturity, Period(frequency), calendar,
convention, convention, DateGeneration::Backward, false);
// Create the fixed-rate bond
FixedRateBond bond(settlementDays, faceValue, schedule,
std::vector<Rate>{couponRate}, dayCounter, convention);
// Build a flat yield curve (4.5%)
Handle<YieldTermStructure> yieldCurve(
boost::make_shared<FlatForward>(today, 0.045, dayCounter));
// Set the pricing engine using the discounting curve
bond.setPricingEngine(boost::make_shared<DiscountingBondEngine>(yieldCurve));
// Output the results
std::cout << std::fixed << std::setprecision(2);
std::cout << "Clean Price : " << bond.cleanPrice() << std::endl;
std::cout << "Dirty Price : " << bond.dirtyPrice() << std::endl;
std::cout << "Accrued Interest: " << bond.accruedAmount() << std::endl;
return 0;
}
After compilation and run, we get:
➜ build ./pricer
Clean Price : 102.01
Dirty Price : 102.04
Accrued Interest: 0.03
🔹 Clean Price = 102.01
This is the market-quoted price of the bond, excluding any interest that has accrued since the last coupon date.
It means that the bond is trading at 102.01% of its face value — i.e., £1,020.10 for a bond with a £1,000 face value.
🔹 Dirty Price = 102.04
This is the actual amount you’d pay if you bought the bond today.
It includes both:
- The clean price (102.01), and
- The interest the seller has “earned” since the last coupon.
So:
[math]
\text{Dirty Price} = \text{Clean Price} + \text{Accrued Interest} = 102.01 + 0.03 = 102.04
[/math]
🔹 Accrued Interest = 0.03
This is the amount of coupon interest that has accrued since the last coupon date but hasn’t been paid yet.
The buyer pays this to the seller because the seller has held the bond for part of the current coupon period.
In your case, it’s 0.03% of face value, or £0.30 on a £1,000 bond — meaning you’re very close to the previous coupon date.