CDS pricer

by

 

06 Nov 2008 (Updated )

This short routine calculates the mark-to-market price of a credit default swap.

cds_price(initDate, endDate, initSpread, curSpreads, discountrate, recovery, freq, useAcc, dcc)
function [MTM RPV01 UFF] = cds_price(initDate, endDate, initSpread, curSpreads, discountrate, recovery, freq, useAcc, dcc)
% 
% Version 0.2
% Author: Rogier Swierstra
% rogier.swierstra@pggm.nl
%
% function MTM =  cds_price(initDate, endDate, initSpread, curSpreads,
%                           discountrate, recovery, freq, useAcc, dcc)
%
% CDS_PRICE calculates the MTM price of a CDS deal, given change in spreads
% It is based on the pricing model of Dominic O'Kane and Stuart Turnbull,
% "Valuation of Credit Default Swaps" (April, 2003) available at:
% http://www.nuclearphynance.com/User%20Files/256/cds.pdf
%
% The input variables are as follows:
% - dates are in Matlab format 'DD-MMM-YY'
% - spreads are in BPs (basepoints)
% - recovery is the recovery assumption fraction (decimal)
% - discountrate is either a fixed discount rate, or a term structure
% - freq is the frequency of coupon payments, per year
% - useAcc is a boolean, whether to compute with accrued payments
% - dcc is the choice of day count convention, default ACT/ACT
% 
% The result of the calculation is the change in value for the protection
% buyer, i.e. an increase in spreads gives a positive MTM. 
%
% RPV01 is the risky present value of 1bp ending at a default 
% UFF is the up-front-fee corresponding to 500bp running spread
%
% example: cds_price('01-Jul-2008','01-Jul-2013',600,2000,.04,6,4,1,0)
%
% This function requires financial toolbox for date manipulations.

% Define primary variables
if nargin < 9, dcc = 0; end        % default: use ACT/ACT
if nargin < 8, useAcc = 1; end     % default: use accrued payments
if nargin < 7, freq = 4; end       % default: quarterly installments
if nargin < 6, recovery = 0.4; end % default: 40% recovery

% Date determinations
% cfdates, daysdif and yearfrac are FT-functions
curDate     = datenum(date());     % or specify the current date
start       = datenum(initDate);
firstcoupon = addtodate(start, 12/freq, 'month');
coupons     = [start cfdates(initDate,endDate,freq,dcc,0,initDate,firstcoupon)];
thisperiod  = find(coupons<curDate, 1,'last');
periods     = yearfrac(coupons(1:end-1), coupons(2:end),dcc);
accrperiod  = accrfrac(coupons(thisperiod),curDate,freq,dcc);

% Determine the discount vector
if isscalar(discountrate) % fixed rate
    discount = exp(-discountrate*periods);
elseif isvector(discountrate) && length(discountrate) == length(periods) % supplied term structure
    discount = cumprod(1./(1+discountrate).^periods);
else error('Discount vector of incorrect size')
end % if

% Compute survival probabilities.
% Note the conversion from basis points to decimal notation!
curTerms = terms(curSpreads, length(periods), freq); % see functions below
q = hazardrate(curTerms/10000, discount, recovery, periods, useAcc); % see functions below

% Risky present value of a basis point, and net mark-to-market value of a
% long position (protection buyer)
RPV01 = discount.*periods*(q(2:end) - useAcc*diff(q)/2)'/10000;
marketSpread = curTerms(thisperiod);
MTM = (marketSpread - initSpread)*RPV01 + useAcc*accrperiod*initSpread/10000;
UFF = MTM + (initSpread - 500)*RPV01;
end % function cds_price


% Given spreads, determine the survival probabilities during each period.
% Strictly speaking, this is not the hazard rate.
%
% This calculation cannot be explained in a comment line, but see the
% referenced article, section 9, and solve inductively for 
%   q(t) = exp(-lambda tau)
% Note that if accr=1 we do assume accrual payments, at halfway the
% timestep. If accr=0, we assume no accrual payments.
function q = hazardrate(spreads, disc, recov, d, accr)
    n = length(disc);   % time periods
    lgd = 1-recov;      % loss given default
    q = zeros(1, n+1);  % survival probabilities
    DL = zeros(1, n);   % Default Leg
    ppl = zeros(1, n);  % pre-spread premium Leg
    PL = zeros(1, n);   % Premium Leg, including accrual terms

    q(1) = 1; s = spreads(1);
    alpha = lgd - accr*s*d(1)/2;
    q(2) = alpha/(alpha + s*d(1));
    
    a = (lgd - (accr/2)*spreads.*d).*disc;
    b = (lgd + (1-accr/2)*spreads.*d).*disc;
    
    for i = 2:n
        DL(i) = DL(i-1) + lgd*disc(i)*(q(i-1)-q(i));
        ppl(i)= ppl(i-1) + disc(i)*(q(i)*d(i) + accr*d(i)*((q(i-1)-q(i))/2)); 
        PL(i) = ppl(i)*spreads(i);
        % a = lgd - (accr/2)*spreads(i)*d(i) ;
        % b = lgd + (1-accr/2)*spreads(i)*d(i);
        q(i+1) = (DL(i)-PL(i) + q(i)*a(i))/b(i);    
    end % for
end % function hazardrate


% Given spreads (5Y/1-3-5-7-10Y/term structure) in bps, convert to a vector
% of suitable length 
function spreads = terms(spr, n, f)
    if isscalar(spr) % Only CDS5Y
        spreads = repmat(spr, 1, n);
    elseif length(spr) == 5 % spreads for 1Y 3Y 5Y 7Y 10Y
        temp = [repmat(spr(1),1,f) repmat(spr(2),1,2*f) repmat(spr(3),1,2*f) repmat(spr(4),1,2*f) repmat(spr(5),1,3*f)];
        spreads = temp(1:n);
    elseif length(spr) == 6 % spreads for 1Y 2Y 3Y 5Y 7Y 10Y
        temp = [repmat(spr(1),1,f) repmat(spr(2),1,f) repmat(spr(3),1,f) repmat(spr(4),1,2*f) repmat(spr(5),1,2*f) repmat(spr(6),1,3*f)];
        spreads = temp(1:n);
    elseif length(spr) == n % if a complete spread structure is available
        spreads = spr;
    end % if
end % function terms

% NOTES
%
% * The function "terms" still works best with flat spreads. No 
% interpolation is done between given data points.
%
% * Fractional maturities may still cause problems.
%

% Author: Rogier Swierstra
% rogier.swierstra@pggm.nl

Contact us