/******************************************************************************
Online C++ Compiler.
Code, Compile, Run and Debug C++ program online.
Write your code in this editor and press "Run" button to compile and execute it.
*******************************************************************************/
#include <iostream>
#include <iomanip>
#include <cmath>
#include <random>
struct CDSResult
{
double premiumPV;
double protectionPV;
double totalPV;
double riskyAnnuity;
double parSpread;
double cs01;
};
// Survival probability
double survival(double lambda, double t)
{
return std::exp(-lambda * t);
}
// Discount factor
double discount(double r, double t)
{
return std::exp(-r * t);
}
// Analytical Price for CDS
CDSResult priceCDS(
bool isProtectionBuyer, // true = buy protection
double notional, // e.g. 1000000 (USD 1mm)
double spread, // e.g. 0.0045 for 45bps
double maturity, // e.g. 5.0
double frequency, // e.g. 0.25
double lambda, // hazard rate
double r, // discount rate
double recoveryRate) // e.g. 0.4
{
double premiumPV = 0.0;
double protectionPV = 0.0;
double riskyAnnuity = 0.0;
int nCoupons = static_cast<int>(maturity / frequency);
for (int i = 1; i <= nCoupons; ++i)
{
double t = i * frequency;
double t_prev = (i - 1) * frequency;
double ProbSurvive = survival(lambda, t);
double DF = discount(r, t);
// Premium leg (paid if alive)
premiumPV += notional * spread * frequency * ProbSurvive * DF;
riskyAnnuity += notional * frequency * ProbSurvive * DF;
}
protectionPV = (1 - recoveryRate) * lambda * riskyAnnuity; // notional inside annuity
// Adjust sign depending on buy/sell protection
double totalPV;
if (isProtectionBuyer)
totalPV = protectionPV - premiumPV;
else
totalPV = premiumPV - protectionPV;
// Par spread = protection leg / risky annuity
double parSpread = protectionPV / riskyAnnuity;
// CS01 = risky annuity * 1bp * notional
double cs01 = riskyAnnuity * 0.0001;
return {premiumPV, protectionPV, totalPV, riskyAnnuity, parSpread, cs01};
}
// Price CDS using Monte Carlo and the CIR Model
CDSResult priceCDS_CIR_MC(
bool isProtectionBuyer,
double notional,
double spread,
double maturity,
double frequency,
double r,
double recoveryRate,
double lambda0,
double kappa,
double theta,
double sigma,
int nSims = 1000)
{
int nSteps = static_cast<int>(maturity / frequency);
std::mt19937 rng(42);
std::normal_distribution<> norm(0.0, 1.0);
double premiumPV_MC = 0.0;
double protectionPV_MC = 0.0;
double riskyAnnuity_MC = 0.0;
for (int sim = 0; sim < nSims; ++sim)
{
double lambda = lambda0;
double survivalProb = 1.0;
double premiumPV = 0.0;
double protectionPV = 0.0;
double riskyAnnuity = 0.0;
for (int i = 1; i <= nSteps; ++i)
{
double t = i * frequency;
double dt = frequency;
// --- Simulate CIR (Euler with full truncation) ---
double Z = norm(rng);
lambda = std::max(0.0, lambda + kappa * (theta - lambda) * dt + sigma * std::sqrt(lambda * dt) * Z);
// --- Update survival ---
survivalProb *= std::exp(-lambda * dt);
double DF = std::exp(-r * t);
// --- Premium leg ---
premiumPV += notional * spread * dt * survivalProb * DF;
riskyAnnuity += notional * dt * survivalProb * DF;
// --- Protection leg ---
protectionPV += notional * (1.0 - recoveryRate) * lambda * survivalProb * dt * DF;
}
premiumPV_MC += premiumPV;
protectionPV_MC += protectionPV;
riskyAnnuity_MC += riskyAnnuity;
}
// Average over simulations
premiumPV_MC /= nSims;
protectionPV_MC /= nSims;
riskyAnnuity_MC /= nSims;
double totalPV = isProtectionBuyer ? (protectionPV_MC - premiumPV_MC) : (premiumPV_MC - protectionPV_MC);
double parSpread = protectionPV_MC / riskyAnnuity_MC;
double cs01 = riskyAnnuity_MC * 0.0001;
return {premiumPV_MC, protectionPV_MC, totalPV, riskyAnnuity_MC, parSpread, cs01};
}
// Price CDS using Default Time Monte Carlo Method
// Uniform variates are interpreted as survival probs and we compute default time from inverse of survival function
CDSResult priceCDS_DefaultTime_MC(
bool isProtectionBuyer,
double notional,
double spread,
double maturity,
double frequency,
double lambda,
double r,
double recoveryRate,
int nSims = 1000)
{
std::mt19937 rng(42);
std::uniform_real_distribution<> uni(0.0, 1.0);
int nCoupons = static_cast<int>(maturity / frequency);
double premiumPV_MC = 0.0;
double protectionPV_MC = 0.0;
double riskyAnnuity_MC = 0.0;
// =========================================================
// STRATIFIED SAMPLING LOOP
// Each simulation gets a guaranteed probability bucket
// =========================================================
for (int sim = 0; sim < nSims; ++sim)
{
// -----------------------------------------------------
// 1. Create stratified uniform variable
//
// Instead of U ~ Uniform(0,1),
// we sample inside fixed interval:
//
// U ∈ [sim/N, (sim+1)/N]
// -----------------------------------------------------
double u_low = (double)sim / nSims;
double u_high = (double)(sim + 1) / nSims;
double U = u_low + (u_high - u_low) * uni(rng);
// -----------------------------------------------------
// 2. Invert survival function to get default time
// -----------------------------------------------------
// Instead of stratifying U, stratify default time directly
// Now stratification happens in: exponential space (linear in hazard time)
double E = -std::log(U);
double tau = -std::log(U) / lambda;
double premiumPV = 0.0;
double protectionPV = 0.0;
double riskyAnnuity = 0.0;
// -----------------------------------------------------
// 3. Premium leg (deterministic once tau known)
// -----------------------------------------------------
for (int i = 1; i <= nCoupons; ++i)
{
double t = i * frequency;
if (tau > t)
{
double DF = discount(r, t);
premiumPV += notional * spread * frequency * DF;
riskyAnnuity += notional * frequency * DF;
}
else
{
break;
}
}
// -----------------------------------------------------
// 4. Protection leg
// -----------------------------------------------------
if (tau <= maturity)
{
double DF_tau = discount(r, tau);
protectionPV = notional * (1.0 - recoveryRate) * DF_tau;
}
// -----------------------------------------------------
// 5. Accumulate
// -----------------------------------------------------
premiumPV_MC += premiumPV;
protectionPV_MC += protectionPV;
riskyAnnuity_MC += riskyAnnuity;
}
// =========================================================
// 6. Average results
// =========================================================
premiumPV_MC /= nSims;
protectionPV_MC /= nSims;
riskyAnnuity_MC /= nSims;
double totalPV = isProtectionBuyer ? (protectionPV_MC - premiumPV_MC) : (premiumPV_MC - protectionPV_MC);
double parSpread = protectionPV_MC / riskyAnnuity_MC;
double cs01 = riskyAnnuity_MC * 0.0001;
return {premiumPV_MC, protectionPV_MC, totalPV, riskyAnnuity_MC, parSpread, cs01};
}
int main()
{
// Example: 5Y CDS on AAPL
bool buyProtection = true;
double notional = 1000000.0; // 1mm
double spread = 0.0045; // 45bps
double maturity = 5.0;
double frequency = 0.25;
double lambda = 0.0075; // 2% hazard rate
double r = 0.03; // 3% discount rate
double recoveryRate = 0.4; // 40% recovery rate
CDSResult res = priceCDS(
buyProtection,
notional,
spread,
maturity,
frequency,
lambda,
r,
recoveryRate);
std::cout << "\n*** CDS Analytical Price ***" << std::endl;
std::cout << std::fixed << std::setprecision(2);
std::cout << "Premium PV: " << res.premiumPV << std::endl;
std::cout << "Protection PV: " << res.protectionPV << std::endl;
std::cout << "Total PV: " << res.totalPV << std::endl;
std::cout << "Risky Annuity: " << res.riskyAnnuity << std::endl;
std::cout << "Par Spread: " << res.parSpread * 10000 << " bps" << std::endl;
std::cout << "CS01: " << res.cs01 << std::endl;
CDSResult resMC = priceCDS_CIR_MC(
buyProtection,
notional,
spread,
maturity,
frequency,
r,
recoveryRate,
0.0075, // lambda0 - initial hazard rate
1.0, // kappa - mean reversion speed
0.0075, // theta - long-run mean
0.02, // sigma - volatility
1000); // Number of Monte Carlo Simulations
std::cout << "\n*** CDS Monte Carlo Price - Using CIR Model ***" << std::endl;
std::cout << std::fixed << std::setprecision(2);
std::cout << "Premium PV: " << resMC.premiumPV << std::endl;
std::cout << "Protection PV: " << resMC.protectionPV << std::endl;
std::cout << "Total PV: " << resMC.totalPV << std::endl;
std::cout << "Risky Annuity: " << resMC.riskyAnnuity << std::endl;
std::cout << "Par Spread: " << resMC.parSpread * 10000 << " bps" << std::endl;
std::cout << "CS01: " << resMC.cs01 << std::endl;
CDSResult resDT = priceCDS_DefaultTime_MC(
buyProtection,
notional,
spread,
maturity,
frequency,
lambda,
r,
recoveryRate,
5000);
std::cout << "\n*** CDS Monte Carlo Price - Using Default Time Approach ***" << std::endl;
std::cout << "Premium PV: " << resDT.premiumPV << std::endl;
std::cout << "Protection PV: " << resDT.protectionPV << std::endl;
std::cout << "Total PV: " << resDT.totalPV << std::endl;
std::cout << "Risky Annuity: " << resDT.riskyAnnuity << std::endl;
std::cout << "Par Spread: " << resDT.parSpread * 10000 << " bps" << std::endl;
std::cout << "CS01: " << resDT.cs01 << std::endl;
return 0;
}