Fixed Income Pricing & Risk Analytics
Fixed-income instruments form the bedrock of institutional quantitative finance. SabiQuant provides an analytical bond valuation engine built on std with zero external dependencies and numerical parity verified against QuantLib.
Mathematical Formulation
1. Dirty Price Valuation
For a bond with cash flows (coupons and face value redemption) payable at times (measured in years from settlement according to the chosen day-count convention), the dirty (full) price given a compounded annual yield to maturity with compounding frequency is expressed as:
where the discount factor depends on the compounding convention:
-
Periodic Compounding ( times per year):
-
Continuous Compounding:
2. Clean Price and Accrued Interest
Market trading conventions quote bond prices as Clean Price (), removing the accrued interest () that accumulates between coupon payment dates:
Accrued interest is computed from the previous coupon date , settlement date , and next coupon date :
where is the gross coupon payment for the period.
3. Analytical Risk Measures
Rather than relying on finite-difference approximations () which introduce numerical truncation error, SabiQuant calculates exact analytical derivatives:
First Derivative () and Modified Duration
Second Derivative () and Convexity
Practical Rust Example
Below is a complete, production-ready example demonstrating how to construct a BondPricingPlan and extract pricing and risk metrics.
use sabiquant::bonds::{BondPricingPlan, BondPricingResult};use sabiquant::core::{Date, Frequency};use sabiquant::core::calendars::TargetCalendar;use sabiquant::core::daycount::ActualActualICMA;
fn main() -> Result<(), Box<dyn std::error::Error>> { // 1. Establish settlement date and bond maturity let settlement = Date::from_ymd(2025, 6, 15)?; let maturity = Date::from_ymd(2035, 6, 15)?;
// 2. Instantiate market calendar and day-count convention let calendar = TargetCalendar::default(); let day_counter = ActualActualICMA::default();
// 3. Build the BondPricingPlan // 10-year 5.00% semi-annual bond, par = 100.0 let plan = BondPricingPlan::builder() .settlement(settlement) .maturity(maturity) .coupon_rate(0.050) // 5.00% .frequency(Frequency::SemiAnnual) .calendar(calendar) .day_counter(day_counter) .face_value(100.0) .build()?;
// 4. Calculate pricing and risk at 4.75% yield let yield_to_maturity = 0.0475; let metrics: BondPricingResult = plan.price_from_yield(yield_to_maturity)?;
println!("================ BOND VALUATION ================"); println!("Dirty Price: ${:.5}", metrics.dirty_price); println!("Clean Price: ${:.5}", metrics.clean_price); println!("Accrued Interest: ${:.5}", metrics.accrued_interest); println!("Macaulay Duration: {:.4} years", metrics.macaulay_duration); println!("Modified Duration: {:.4}", metrics.modified_duration); println!("Convexity: {:.4}", metrics.convexity); println!("dP/dy (DV01 base): {:.4}", metrics.dp_dy); println!("================================================");
// 5. Inverting Yield from Market Clean Price // Using Brent's robust root-finding method under the hood let market_clean_price = 101.95; let implied_ytm = plan.yield_from_clean_price(market_clean_price)?; println!("Implied Market YTM: {:.4}%", implied_ytm * 100.0);
Ok(())}Output Metrics Reference
| Metric | Type | Description |
|---|---|---|
dirty_price | f64 | Full settlement price including accrued coupon () |
clean_price | f64 | Quoted market price excluding accrued coupon () |
accrued_interest | f64 | Accrued coupon earned from last payment date to settlement |
macaulay_duration | f64 | Weighted average maturity of cash flows in years () |
modified_duration | f64 | Percentage price sensitivity per unit change in yield () |
convexity | f64 | Second derivative curvature measure () |
dp_dy | f64 | Direct analytical first derivative |