Charges to parameters of SABR model for a swaption

Charges to parameters of SABR model for a swaption

Manage alerts

Loading saved threads...

Jonita D'souza · External communityPost link
External question — Quantitative Finance Stack Exchange Author: Jonita D'souza Original post: https://quant.stackexchange.com/questions/85534 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. I have forward premium for a Swaption. How will I allocate the charges to parameters of SABR model for risk based Liquidity Reserve Calculation? So, I just want to understand how forward bid-offer spreads quoted by brokers are allocated to SABR parameter, to understand the liquidity cost.
Quote
Report
Dimitri Vulis · External communityPost link
External answer — Quantitative Finance Stack Exchange Author: Dimitri Vulis Original post: https://quant.stackexchange.com/a/85536 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. SABR model uses 3 model parameters: alpha is the at the money vol, rho is the skew, nu is the vol of vol. But this discussion can be generalized to other pricing models. The brokers quote bid-offer spreads for different strikes - out of the money, at the money, in the money. The model fair value of a swaption depends on these 3 model parameters and on the forward rate. Calculate the sensitivities of the model fair value to each model parameter. These are sometimes called "model greeks". You can calculate these numerically by bumping the model parameters up and down a little and re-running the pricing model. The sensitivity of the fair value to the alpha is like vega. Having the sensitivities, you have (at least) two methodologies to set the liquidity reserve. The result shouldn't be very different between them. bottom up - invert the Jacobian matrix that relates the bid-offer spreads quoted by brokers and the sensitivities to the model parameters. top down - from broker quotes, calibrate the model parameters from all bid side quotes and all offer side quotes. The liquidity reserve for each model parameter is the sensitivity to this parameter times the difference between "offer" and "bid" values for this parameter.
Quote
Report
pandashark · External communityPost link
External answer — Quantitative Finance Stack Exchange Author: pandashark Original post: https://quant.stackexchange.com/a/85569 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. In short: calibrate SABR to bid and offer quotes separately, compare the fitted parameters, and multiply each parameter difference by the position's sensitivity to that parameter. The worked example below shows how. Building on Dimitri Vulis's answer, here is a worked example using QuantLib . Setup In SABR, $\beta$ is typically fixed by convention (commonly 0 or 0.5 for rates), leaving three free parameters: $\alpha$ — ATM vol level $\nu$ — vol-of-vol, controls smile curvature (butterfly) $\rho$ — correlation, controls skew (risk reversal) Given broker-quoted bid-offer spreads across strikes, how much of the liquidity cost is attributable to each parameter? Approach 1: Top-Down (Separate Bid/Offer Calibration) Calibrate SABR to bid vols and offer vols independently. The parameter differences give per-parameter uncertainty bands. Using QuantLib's SABRInterpolation on a stylized 5Y10Y swaption smile ( $\beta=0.5$ fixed): alpha nu rho Mid: 0.0284 0.4473 -0.0478 Bid: 0.0288 0.3920 -0.0888 Offer: 0.0281 0.4996 -0.0130 ───────────────────────────────────── Δ(param): -0.0007 0.1076 0.0757 The liquidity charge per parameter is $\left|\frac{\partial V}{\partial \theta_j}\right| \times |\Delta\theta_j|$ , where the model greeks are computed by bumping each SABR parameter and repricing via sabrVolatility() + blackFormula() : ATM liquidity reserve decomposition (per unit notional): alpha charge = 0.0115 bp (38%) nu charge = 0.0170 bp (56%) rho charge = 0.0019 bp ( 6%) TOTAL = 0.0304 bp Caveat: This approach can be fragile. SABR calibration is nonlinear, and small perturbations in input vols can produce disproportionate parameter shifts, especially for $\nu$ and $\rho$ which are sensitive to wing data. Some desks regularize by constraining certain parameters across bid/offer. Approach 2: Bottom-Up (Jacobian Decomposition) Calibrate SABR to mid vols once, then compute the Jacobian $J_{ij} = \frac{\partial \sigma_i}{\partial \theta_j}$ at each strike. Convert to price space via vega: Strike BidOffer$ Alpha$ Nu$ Rho$ Total$ ────────────────────────────────────────────────────────── 1.0% 0.000160 0.000026 0.000239 0.000040 0.000305 2.0% 0.000151 0.000068 0.000296 0.000069 0.000433 2.5% 0.000131 0.000098 0.000229 0.000048 0.000374 3.0% 0.000105 0.000115 0.000170 0.000019 0.000304 ← ATM 3.5% 0.000157 0.000108 0.000220 0.000088 0.000416 4.0% 0.000233 0.000088 0.000305 0.000118 0.000511 5.0% 0.000508 0.000056 0.000374 0.000111 0.000542 Interpretation The table shows the SABR structure at work: ATM : $\alpha$ charge dominates — $\alpha$ sets the ATM vol level, so ATM liquidity cost is mostly vol uncertainty Wings (deep OTM): $\nu$ charge dominates — vol-of-vol controls smile curvature Skew (asymmetry between low/high strikes): $\rho$ contributes — it controls the tilt This decomposition is a first-order approximation . The sum of per-parameter charges won't exactly equal the total bid-offer spread due to: (1) cross-terms in the Taylor expansion, (2) the overdetermined system (more strikes than parameters) leaves a least-squares residual, and (3) the market smile is not exactly SABR-shaped. Many desks add an "unexplained residual" bucket. A practical alternative is bump-and-reprice, which captures nonlinearities automatically. Regulatory Context This decomposition applies to Prudent Valuation (PVA) under CRR2 Article 105, where banks compute close-out cost AVAs from bid-offer spreads. It also informs FRTB IMA liquidity horizon assignments. QuantLib Code #include <ql/quantlib.hpp> using namespace QuantLib; // Market data: 5Y10Y swaption smile Real forward = 0.03; Time expiry = 5.0; std::vector<Real> strikes = {0.01, 0.02, 0.025, 0.03, 0.035, 0.04, 0.05}; std::vector<Real> midVols = {0.35, 0.22, 0.195, 0.18, 0.175, 0.178, 0.195}; // Calibrate SABR (beta=0.5 fixed) SABRInterpolation sabr( strikes.begin(), strikes.end(), midVols.begin(), expiry, forward, Null<Real>(), 0.5, Null<Real>(), Null<Real>(), false, true, false, false, // alpha,nu,rho free; beta fixed true, // vega-weighted ext::make_shared<EndCriteria>(100000, 100, 1e-8, 1e-8, 1e-8)); sabr.update(); // sabr.alpha(), sabr.nu(), sabr.rho() now hold calibrated params // Model greeks by finite difference Real bump = 1e-4; Real volUp = sabrVolatility(K, forward, expiry, alpha+bump, beta, nu, rho); Real volDn = sabrVolatility(K, forward, expiry, alpha-bump, beta, nu, rho); Real dVoldAlpha = (volUp - volDn) / (2.0 * bump); // Convert to price: dPrice/dParam = vega * dVol/dParam Real vega = blackFormulaStdDevDerivative(K, forward, midVol*sqrt(expiry)) * sqrt(expiry); Real chargeAlpha = fabs(vega * dVoldAlpha * deltaAlpha); Full working example (~200 lines, both approaches) /* Demonstrates how to decompose swaption bid-offer spreads into SABR parameter charges for liquidity reserve calculation. Two approaches: 1. Top-down: calibrate SABR to bid vols and offer vols separately, then compute per-parameter reserves from the parameter differences. 2. Bottom-up (Jacobian): compute sensitivities of option prices to SABR parameters, invert to map bid-offer price spreads back to parameter space. */ #include <ql/quantlib.hpp> #include <iostream> #include <iomanip> #include <vector> #include <cmath> using namespace QuantLib; int main() { std::cout << std::fixed << std::setprecision(6); // --------------------------------------------------------------- // Market data: 5Y10Y swaption smile (lognormal vols) // --------------------------------------------------------------- Real forward = 0.03; // 3% forward swap rate Time expiry = 5.0; // 5Y option expiry // Strikes: -200bp to +200bp around forward std::vector<Real> strikes = {0.01, 0.02, 0.025, 0.03, 0.035, 0.04, 0.05}; // Mid-market vols (lognormal) std::vector<Real> midVols = {0.35, 0.22, 0.195, 0.18, 0.175, 0.178, 0.195}; // Bid-offer half-spreads in vol points (wider for wings) std::vector<Real> halfSpreadVol = {0.015, 0.005, 0.003, 0.002, 0.003, 0.005, 0.015}; // Construct bid and offer vol vectors std::vector<Real> bidVols(strikes.size()), offerVols(strikes.size()); for (Size i = 0; i < strikes.size(); ++i) { bidVols[i] = midVols[i] - halfSpreadVol[i]; offerVols[i] = midVols[i] + halfSpreadVol[i]; } // --------------------------------------------------------------- // SABR calibration: mid, bid, offer // --------------------------------------------------------------- // Fix beta = 0.5 (common convention for rates) Real betaFixed = 0.5; auto calibrate = [&](const std::vector<Real>& vols, const std::string& label) { SABRInterpolation sabr( strikes.begin(), strikes.end(), vols.begin(), expiry, forward, Null<Real>(), betaFixed, Null<Real>(), Null<Real>(), false, true, false, false, // alpha,nu,rho free; beta fixed true, // vega weighted ext::shared_ptr<EndCriteria>(new EndCriteria(100000, 100, 1e-8, 1e-8, 1e-8)), ext::shared_ptr<OptimizationMethod>(), 0.0020, false, 50, 0.0, VolatilityType::ShiftedLognormal); sabr.update(); std::cout << label << ":\n" << " alpha = " << sabr.alpha() << " beta = " << sabr.beta() << " nu = " << sabr.nu() << " rho = " << sabr.rho() << " (rms err = " << sabr.rmsError() << ")\n"; return std::make_tuple(sabr.alpha(), sabr.beta(), sabr.nu(), sabr.rho()); }; std::cout << "=== SABR Calibration Results ===\n\n"; auto [alphaMid, betaMid, nuMid, rhoMid] = calibrate(midVols, "Mid"); auto [alphaBid, betaBid, nuBid, rhoBid] = calibrate(bidVols, "Bid"); auto [alphaOffer, betaOffer, nuOffer, rhoOffer] = calibrate(offerVols, "Offer"); // --------------------------------------------------------------- // Approach 1: Top-down parameter charge decomposition // --------------------------------------------------------------- std::cout << "\n=== Approach 1: Top-Down (Calibrate Bid & Offer Separately) ===\n\n"; Real dAlpha = alphaOffer - alphaBid; Real dNu = nuOffer - nuBid; Real dRho = rhoOffer - rhoBid; std::cout << "Parameter bid-offer ranges:\n" << " delta(alpha) = " << dAlpha << "\n" << " delta(nu) = " << dNu << "\n" << " delta(rho) = " << dRho << "\n"; // Compute model greeks (sensitivities) by finite difference on ATM price Real bump = 1e-4; Real atmMidVol = sabrVolatility(forward, forward, expiry, alphaMid, betaMid, nuMid, rhoMid); Real atmPrice = blackFormula(Option::Call, forward, forward, atmMidVol * std::sqrt(expiry)); // d(price)/d(alpha) Real volUp = sabrVolatility(forward, forward, expiry, alphaMid + bump, betaMid, nuMid, rhoMid); Real volDn = sabrVolatility(forward, forward, expiry, alphaMid - bump, betaMid, nuMid, rhoMid); Real dPdAlpha = (blackFormula(Option::Call, forward, forward, volUp * std::sqrt(expiry)) - blackFormula(Option::Call, forward, forward, volDn * std::sqrt(expiry))) / (2.0 * bump); // d(price)/d(nu) volUp = sabrVolatility(forward, forward, expiry, alphaMid, betaMid, nuMid + bump, rhoMid); volDn = sabrVolatility(forward, forward, expiry, alphaMid, betaMid, nuMid - bump, rhoMid); Real dPdNu = (blackFormula(Option::Call, forward, forward, volUp * std::sqrt(expiry)) - blackFormula(Option::Call, forward, forward, volDn * std::sqrt(expiry))) / (2.0 * bump); // d(price)/d(rho) volUp = sabrVolatility(forward, forward, expiry, alphaMid, betaMid, nuMid, std::min(rhoMid + bump, 0.9999)); volDn = sabrVolatility(forward, forward, expiry, alphaMid, betaMid, nuMid, std::max(rhoMid - bump, -0.9999)); Real dPdRho = (blackFormula(Option::Call, forward, forward, volUp * std::sqrt(expiry)) - blackFormula(Option::Call, forward, forward, volDn * std::sqrt(expiry))) / (2.0 * bump); std::cout << "\nATM model greeks (sensitivities to SABR params):\n" << " dPrice/dAlpha = " << dPdAlpha << "\n" << " dPrice/dNu = " << dPdNu << "\n" << " dPrice/dRho = " << dPdRho << "\n"; Real chargeAlpha = std::fabs(dPdAlpha * dAlpha); Real chargeNu = std::fabs(dPdNu * dNu); Real chargeRho = std::fabs(dPdRho * dRho); Real totalCharge = chargeAlpha + chargeNu + chargeRho; std::cout << "\nLiquidity reserve decomposition (ATM, per unit notional):\n" << " alpha charge = " << chargeAlpha << " (" << 100.0 * chargeAlpha / totalCharge << "%)\n" << " nu charge = " << chargeNu << " (" << 100.0 * chargeNu / totalCharge << "%)\n" << " rho charge = " << chargeRho << " (" << 100.0 * chargeRho / totalCharge << "%)\n" << " TOTAL = " << totalCharge << "\n"; // --------------------------------------------------------------- // Approach 2: Bottom-up Jacobian at multiple strikes // --------------------------------------------------------------- std::cout << "\n=== Approach 2: Bottom-Up (Jacobian at Each Strike) ===\n\n"; std::cout << std::setw(10) << "Strike" << std::setw(12) << "BidOffer" << std::setw(12) << "Alpha$" << std::setw(12) << "Nu$" << std::setw(12) << "Rho$" << std::setw(12) << "Total$" << "\n"; std::cout << std::string(70, '-') << "\n"; for (Size i = 0; i < strikes.size(); ++i) { Real K = strikes[i]; Real bidOfferPrice = blackFormula(Option::Call, K, forward, offerVols[i] * std::sqrt(expiry)) - blackFormula(Option::Call, K, forward, bidVols[i] * std::sqrt(expiry)); // Jacobian row: d(vol)/d(param) at this strike, using mid params auto sabrVol = [&](Real a, Real n, Real r) { return sabrVolatility(K, forward, expiry, a, betaMid, n, r); }; Real dvdA = (sabrVol(alphaMid + bump, nuMid, rhoMid) - sabrVol(alphaMid - bump, nuMid, rhoMid)) / (2.0 * bump); Real dvdN = (sabrVol(alphaMid, nuMid + bump, rhoMid) - sabrVol(alphaMid, nuMid - bump, rhoMid)) / (2.0 * bump); Real dvdR = (sabrVol(alphaMid, nuMid, std::min(rhoMid + bump, 0.9999)) - sabrVol(alphaMid, nuMid, std::max(rhoMid - bump, -0.9999))) / (2.0 * bump); // Convert vol sensitivity to price sensitivity via vega Real midVol_i = sabrVol(alphaMid, nuMid, rhoMid); Real vega = blackFormulaStdDevDerivative(K, forward, midVol_i * std::sqrt(expiry)) * std::sqrt(expiry); Real chA = std::fabs(vega * dvdA * dAlpha); Real chN = std::fabs(vega * dvdN * dNu); Real chR = std::fabs(vega * dvdR * dRho); std::cout << std::setw(10) << K << std::setw(12) << bidOfferPrice << std::setw(12) << chA << std::setw(12) << chN << std::setw(12) << chR << std::setw(12) << chA + chN + chR << "\n"; } std::cout << "\nDone.\n"; return 0; } Compile against QuantLib: clang++ -std=c++17 -O2 -I/path/to/quantlib -lQuantLib -o sabr_reserve sabr_liquidity_reserve.cpp
Quote
Report

Post Reply

Quoted from Forex.com.bd-Editorial External answer — Quantitative Finance Stack Exchange Author: Dimitri Vulis Source score (net votes, not local likes): 1 Original post: https://quant.stackexchange.com/a/85536 License: CC BY-SA 4.0 — https://creativecommons.org/licenses/by-sa/4.0/ Adaptation: HTML converted to plain text; contact email addresses removed. SABR model uses 3 model parameters: alpha is the at the money vol, rho is the skew, nu is the vol of vol. But this discussion can be generalized to other pricing models. The brokers quote bid-offer spreads for different strikes - out of the money, at the money, in the money. The model fair value of a swaption depends on these 3 model parameters and on the forward rate. Calculate the sensitivities of the model fair value to each model parameter. These are sometimes called "model greeks". You can calculate these numerically by bumping the model parameters up and down a little and re-running the pricing model. The sensitivity of the fair value to the alpha is like vega. Having the sensitivities, you have (at least) two methodologies to set the liquidity reserve. The result shouldn't be very different between them. bottom up - invert the Jacobian matrix that relates the bid-offer spreads quoted by brokers and the sensitivities to the model parameters. top down - from broker quotes, calibrate the model parameters from all bid side quotes and all offer side quotes. The liquidity reserve for each model parameter is the sensitivity to this parameter times the difference between "offer" and "bid" values for this parameter.

Cancel quote

Checking account access…