Accelerating the pace of engineering and science

# Documentation Center

• Trial Software

## Counterparty Credit Risk and CVA

This example shows how to compute the unilateral credit value (valuation) adjustment (CVA) for a bank holding a portfolio of vanilla interest rate swaps with several counterparties. CVA is the expected loss on an over-the-counter instrument or portfolio of instruments due to counterparty default. The CVA for a particular counterparty is defined as the sum over all points in time of the discounted expected exposure at each moment multiplied by the probability that the counterparty defaults at that moment, all multiplied by 1 minus the recovery rate. The CVA formula is:

Where R is the recovery, discEE the discounted expected exposure at time t, and PD the default probability distribution.

The expected exposure is computed by first simulating many future scenarios of risk factors for the given instrument or portfolio. Risk factors can be interest rates, as in this example, but will differ based on the portfolio and can include FX rates, equity or commodity prices, or anything that will affect the market value of the instruments. Once a sufficient set of scenarios has been simulated, the contract or portfolio can be priced on a series of future dates for each scenario. The result is a matrix, or "cube", of instrument values.

These prices are converted into exposures after taking into account collateral agreements that the bank might have in place as well as netting agreements, as in this example, where the values of several instruments may offset each other, lowering their total exposure.

The instrument values for each scenario are discounted to compute the discounted exposures. The discounted expected exposures can then be computed by a simple average of the discounted exposures at each simulation date.

Finally, counterparty default probabilities are typically derived from credit default swap (CDS) market quotes and the CVA for the counterparty can be computed according to the above formula. We assume that a counterparty default is independent of its exposure (no wrong-way risk).

For this example we will work with a portfolio of vanilla interest rate swaps with the goal of computing the CVA for a particular counterparty.

The portfolio of swaps is close to zero value at time t=0. Each swap is associated with a counterparty and may or may not be included in a netting agreement.

% Read swaps from spreadsheet
swapFile = 'cva-swap-portfolio.xls';
swapData = dataset('XLSFile',swapFile,'Sheet','Swap Portfolio');

swaps = struct(...
'Counterparty',[],...
'NettingID',[],...
'Principal',[],...
'Maturity',[],...
'LegRate',[],...
'LegType',[],...
'LatestFloatingRate',[],...
'FloatingResetDates',[]);

swaps.Counterparty = swapData.CounterpartyID;
swaps.NettingID    = swapData.NettingID;
swaps.Principal    = swapData.Principal;
swaps.Maturity     = swapData.Maturity;
swaps.LegType      = [swapData.LegType ~swapData.LegType];
swaps.LegRate      = [swapData.LegRateReceiving swapData.LegRatePaying];
swaps.LatestFloatingRate = swapData.LatestFloatingRate;
swaps.Period       = swapData.Period;
swaps.LegReset     = ones(size(swaps.LegType));

numSwaps = numel(swaps.Counterparty);
numCounterparties = max(swaps.Counterparty);


Create RateSpec from the Interest Rate Curve

Settle   = datenum('14-Dec-2007');

Tenor  = [3 6 12 5*12 7*12 10*12 20*12 30*12]';
ZeroRates = [0.033 0.034 0.035 0.040 0.042 0.044 0.048 0.0475]';

ZeroDates = datemnth(Settle,Tenor);
Compounding = 2;
Basis = 0;
RateSpec = intenvset('StartDates', Settle,'EndDates', ZeroDates,...
'Rates', ZeroRates,'Compounding',Compounding,'Basis',Basis);

% Create an IRCurve object.  We will use this for computing instantaneous
% forward rates during the calculation of the Hull-White short rate path.
RateCurveObj = IRDataCurve('Zero',Settle,ZeroDates,ZeroRates,...
'Compounding', Compounding,'Basis', Basis);

figure;
plot(ZeroDates, ZeroRates, 'o-');
xlabel('Date');
datetick('keeplimits');
ylabel('Zero rate'); grid on;
title('Yield Curve at Settle Date');


Set Changable Simulation Parameters

We can vary here the number of simulated interest rate scenarios we generate. We set our simulation dates to be more frequent at first, then turning less frequent further in the future.

% Number of Monte Carlo simulations
numScenarios = 1000;

% Compute monthly simulation dates, then quarterly dates later.
simulationDates = datemnth(Settle,0:12);
simulationDates = [simulationDates datemnth(simulationDates(end),3:3:74)]';
numDates = numel(simulationDates);


Compute Floating Reset Dates

For each simulation date, we compute previous floating reset date for each swap.

floatDates = cfdates(Settle-360,swaps.Maturity,swaps.Period);
swaps.FloatingResetDates = zeros(numSwaps,numDates);
for i = numDates:-1:1
thisDate = simulationDates(i);
floatDates(floatDates > thisDate) = 0;
swaps.FloatingResetDates(:,i) = max(floatDates,[],2);
end


Setup Hull-White Single Factor Model

The risk factor we will simulate to value our instruments is the zero curve. For this example we will model the interest rate term structure using the one-factor Hull-White model. This is a model of the short rate and is defined as:

where

• : Change in the short rate after a small change in time,

• : Mean reversion rate

• : Volatility of the short rate

• : A Weiner process (a standard normal process)

• : Drift function defined as:

: Instantaneous forward rate at time

: Partial derivative of with respect to time

Once we have simulated a path of the short rate we generate a full yield curve at each simulation date using the formula:

: Zero rate at time for a period of

: Price of a zero coupon bond at time that pays one dollar at time

Each scenario contains the full term structure moving forward through time, modeled at each of our selected simulation dates.

Refer to "Calibrating the Hull-White Model Using Market Data" example in the Financial Instruments Toolbox™ Users' Guide for more details on Hull-White one-factor model calibration.

Alpha = 0.2;
Sigma = 0.015;

r0 = RateCurveObj.getZeroRates(Settle+1,'Compounding',-1);
t0 = Settle;

% Construct SDE object
FwdRates = RateCurveObj.getForwardRates(t0+1:max(swaps.Maturity),'Compounding',-1);
hullwhite1 = hwv(Alpha,@(t,x) hw1LevelFun(t0,t,FwdRates,Alpha,Sigma),...
Sigma,'StartState',r0) %#ok<NOPTS>

% Store all model calibration information
calibration.RateCurveObj = RateCurveObj;
calibration.Tenor = Tenor;
calibration.ShortRateModel = hullwhite1;
calibration.Alpha = Alpha;
calibration.Sigma = Sigma;

hullwhite1 =

Class HWV: Hull-White/Vasicek
----------------------------------------
Dimensions: State = 1, Brownian = 1
----------------------------------------
StartTime: 0
StartState: 0.0317681
Correlation: 1
Drift: drift rate function F(t,X(t))
Diffusion: diffusion rate function G(t,X(t))
Simulation: simulation method/function simByEuler
Sigma: 0.015
Level: function @(t,x)hw1LevelFun(t0,t,FwdRates,Alpha,Sigma)
Speed: 0.2


Simulate Scenarios

For each scenario, we simulate the future interest rate curve at each valuation date using the Hull-White one-factor interest rate model.

% Use reproducible random number generator (vary the seed to produce
% different random scenarios).
prevRNG = rng(0);

% Compute interest rate scenarios are their respective discount factors
[scenarios, dfactors] = hgenerateScenario(calibration,simulationDates,numScenarios);

% Restore random number generator state
rng(prevRNG);


Inspect a Scenario

Create a surface plot of the yield curve evolution for a particular scenario.

i = 20;
figure;
surf(Tenor, simulationDates, scenarios(:,:,i))
axis tight
datetick('y','mmmyy');
xlabel('Tenor (Months)');
ylabel('Observation Date');
zlabel('Rates');
set(gca,'View',[-49 32]);
title(sprintf('Scenario %d Yield Curve Evolution\n',i));


Compute Mark to Market Swap Prices

For each scenario the swap portfolio is priced at each future simulation date. Prices are computed using a price approximation function, hswapapprox. It is common in CVA applications to use simplified approximation functions when pricing instruments due to the performance requirements of these Monte Carlo simulations.

Since the simulation dates do not correspond to the swaps cash flow dates (where the floating rates are reset) we estimate the latest floating rate with the 1-year rate (all swaps have period 1 year) interpolated between the nearest simulated rate curves.

The swap prices are then aggregated into a "cube" of values which contains all future instrument values at each simulation date for each scenario. The resulting cube of instrument prices is a 3 dimensional matrix where each row represents a simulation date, each column an instrument, and each "page" a different simulated scenario.

% Compute all mark-to-market values for this scenario.  We use an
% approximation function here to improve performance.
values = hcomputeMTMValues(swaps,simulationDates,scenarios,Tenor);


Inspect Scenario Prices

Create a plot of the evolution of all swap prices for a particular scenario.

i = 32;
figure;
plot(simulationDates, values(:,:,i));
datetick;
ylabel('Mark-To-Market Price');
title(sprintf('Swap prices along scenario %d', i));


Visualize Simulated Portfolio Values

We plot the total portfolio value for each scenario of our simulation. As each scenario moves forward in time the values of the instruments will move up or down depending on how the modeled interest rate term structure changes. As the swaps get closer to maturity, their values will begin to approach zero since the aggregate value of all remaining cash flows will decrease after each cash flow date.

% View portfolio value over time
figure;
totalPortValues = squeeze(sum(values, 2));
plot(simulationDates,totalPortValues);
title('Total MTM Portfolio Value for All Scenarios');
datetick('x','mmmyy')
ylabel('Portfolio Value ($)') xlabel('Simulation Dates')  Compute Exposure by Counterparty The exposure of a particular contract (i) at time t is the maximum of the contract value (Vi) and 0: And the exposure for a particular counterparty is simply a sum of the individual contract exposures: In the presence of netting agreements, however, contracts are aggregated together and can offset each other. Therefore the total exposure of all instruments in a netting agreement is: We compute these exposures for each counterparty at each simulation date. Additive exposure is computed at the netting set level. Exposure of an unnetted instrument is equal to the market value of the instrument if the instrument has positive value, otherwise it is zero. Instruments included in a netting agreement have exposure equal to their value (positive or negative) when the netting agreement has a positive aggregate value, otherwise their exposure is zero. A contract in a netting agreement can have a negative additive exposure, meaning its negative market value reduces the overall netting set exposure. See references for more details. instrument_exposures = zeros(size(values)); unnettedIdx = swaps.NettingID == 0; instrument_exposures(:,unnettedIdx,:) = max(values(:,unnettedIdx,:),0); % We compute exposures per netting agreement, but in this case each % counterparty has only a single netting agreement. for i = 1:numCounterparties nettedIdx = swaps.NettingID == i; numInst = sum(nettedIdx); % Exposures for instruments under netting agreements nettingSetValues = values(:,nettedIdx,:); nettedExposure = max(sum(nettingSetValues,2),0); positiveIdx = repmat(nettedExposure > 0,[1 numInst]); % Individual instrument contributions to netting set exposure instrument_exposures(:,nettedIdx,:) = nettingSetValues .* positiveIdx; end % Sum the instrument exposures for each counterparty exposures = zeros(numDates,numCounterparties,numScenarios); for i = 1:numCounterparties cpSwapIdx = swaps.Counterparty == i; exposures(:,i,:) = sum(instrument_exposures(:,cpSwapIdx,:),2); end  We plot the total portfolio exposure for each scenario in our simulation. Similar to the plot of instrument values, the exposures for each scenario will approach zero as the swaps mature. % View portfolio exposure over time figure; totalPortExposure = squeeze(sum(exposures,2)); plot(simulationDates,totalPortExposure); title('Portfolio Exposure for All Scenarios'); datetick('x','mmmyy') ylabel('Exposure ($)')
xlabel('Simulation Dates')


Exposure Profiles

Several exposure profiles are useful when analyzing the potential future exposure of a bank to a counterparty. Here we compute several (non-discounted) exposure profiles per counterparty as well as for the entire portfolio.

• PE : Peak Exposure : A high percentile (95%) of the distribution of exposures at any particular future date. Also called Potential Future Exposure (PFE).

• MPE : Maximum Peak Exposure : The maximum peak exposure across all dates

• EE : Expected Exposure : The mean (average) of the distribution of exposures at each date

• EPE : Expected Positive Exposure : Weighted average over time of the expected exposure

• EffEE : Effective Expected Exposure : The maximum expected exposure at any time, t, or previous time

• EffEPE : Effective Expected Positive Exposure : The weighted average of the effective expected exposure

For further definitions, see for example Basel II document in references.

% Compute entire portfolio exposure
expPort = squeeze(sum(exposures,2));

% Peak Exposure (same as Potential Future Exposure)
PEcp   = prctile(exposures,95,3);
PEport = prctile(expPort,95,2);

% Maximum Peak Exposure
MPEcp   = max(PEcp);
MPEport = max(PEport);

% Expected Exposure
EEcp   = mean(exposures,3);
EEport = mean(expPort,2);

% Expected Positive Exposure: Weighted average over time of EE
% * In continuous time, this is the average expected exposures over time,
%   an integral of EE(t) over the time interval, divided by the length of
%   the interval
% * Compute using a "trapezoidal" approach here
simTimeInterval = yearfrac(Settle, simulationDates, 1);
simTotalTime = simTimeInterval(end)-simTimeInterval(1);
EPEcp   = 0.5*(EEcp(1:end-1,:)+EEcp(2:end,:))' * diff(simTimeInterval)/simTotalTime;
EPEport = 0.5*(EEport(1:end-1)+EEport(2:end))' * diff(simTimeInterval)/simTotalTime;

% Effective Expected Exposure: Max EE up to time simTimeInterval
EffEEcp = zeros(size(EEcp));
for i = 1:size(EEcp,2)

% Compute cumulative maximum
m = EEcp(1,i);
for j = 1:numel(simulationDates)
if EEcp(j,i) > m
m = EEcp(j,i);
end
EffEEcp(j,i) = m;
end

end

% Compute cumulative maximum for portfolio
EffEEport = zeros(size(EEport));
m = EEport(1);
for i = 1:numel(simulationDates)
if EEport(i) > m
m = EEport(i);
end
EffEEport(i) = m;
end

% Effective Expected Positive Exposure: Weighted average over time of EffEE
EffEPEcp   = 0.5*(EffEEcp(1:end-1,:)+EffEEcp(2:end,:))' * diff(simTimeInterval)/simTotalTime;
EffEPEport = 0.5*(EffEEport(1:end-1)+EffEEport(2:end))' * diff(simTimeInterval)/simTotalTime;


We visualize the exposure profiles, first for the entire portfolio, then for a particular counterparty.

% Visualize portfolio exposure profiles
figure;
plot(simulationDates,PEport,...
simulationDates,MPEport*ones(size(PEport)),...
simulationDates,EEport,...
simulationDates,EPEport*ones(size(PEport)),...
simulationDates,EffEEport,...
simulationDates,EffEPEport*ones(size(PEport)))
legend({'Peak Exposure (PE95%)','Max Peak Exposure (MPE95%)',...
'Exp Exposure (EE)','Time-Avg EE (EPE)','Max past EE (EffEE)','Time-Avg EffEE (EffEPE)'})

datetick('x','mmmyy')
title('Portfolio Exposure Profiles');
ylabel('Exposure ($)') xlabel('Simulation Dates')  Visualize exposure profiles for a particular counterparty cpIdx = 5; figure; plot(simulationDates,PEcp(:,cpIdx),... simulationDates,MPEcp(cpIdx)*ones(size(PEcp(:,cpIdx))),... simulationDates,EEcp(:,cpIdx),... simulationDates,EPEcp(cpIdx)*ones(size(PEcp(:,cpIdx))),... simulationDates,EffEEcp(:,cpIdx),... simulationDates,EffEPEcp(cpIdx)*ones(size(PEcp(:,cpIdx)))) legend({'Peak Exposure (PE95%)','Max Peak Exposure (MPE95%)',... 'Exp Exposure (EE)','Time-Avg EE (EPE)','Max past EE (EffEE)','Time-Avg EffEE (EffEPE)'}) datetick('x','mmmyy','keeplimits') title(sprintf('Counterparty %d Exposure Profiles',cpIdx)); ylabel('Exposure ($)')
xlabel('Simulation Dates')


Discounted Exposures

We compute the discounted expected exposures using the discount factors from each simulated interest rate scenario. The discount factor for a given valuation date in a given scenario is the product of the incremental discount factors from one simulation date to the next, along the interest rate path of that scenario.

% Get discounted exposures per counterparty, for each scenario
discExp = zeros(size(exposures));
for i = 1:numScenarios
discExp(:,:,i) = bsxfun(@times,dfactors(:,i),exposures(:,:,i));
end

% Discounted expected exposure
discEE = mean(discExp,3);


We plot the discounted expected exposures for the aggregate portfolio as well as for each counterparty.

% Portfolio discounted EE
figure;
plot(simulationDates,sum(discEE,2))
datetick('x','mmmyy','keeplimits')
title('Discounted Expected Exposure for Portfolio');
ylabel('Discounted Exposure ($)') xlabel('Simulation Dates') % Counterparty discounted EE figure; plot(simulationDates,discEE) datetick('x','mmmyy','keeplimits') title('Discounted Expected Exposure for Each Counterparty'); ylabel('Discounted Exposure ($)')
xlabel('Simulation Dates')


Calibrating Probability of Default Curve for Each Counterparty

The default probability for a given counterparty is implied by the current market spreads of the counterparty's CDS. We use the function cdsbootstrap to generate the cumulative probability of default at each simulation date.

% Import CDS market information for each counterparty
CDSDates = datenum(CDS.Date);

ZeroData = [RateSpec.EndDates RateSpec.Rates];

% Calibrate default probabilities for each counterparty
for i = 1:size(DefProb,2)
Settle, 'probDates', simulationDates);
DefProb(:,i) = probData(:,2);
end

% We plot of the cumulative probability of default for each counterparty.
figure;
plot(simulationDates,DefProb)
title('Default Probability Curve for Each Counterparty');
xlabel('Date');
grid on;
ylabel('Cumulative Probability')
datetick('x','mmmyy')
ylabel('Probability of Default')
xlabel('Simulation Dates')

CDS =

Date               cp1    cp2    cp3    cp4    cp5
'3/20/2008'        140     85    115    170    140
'3/20/2009'        185    120    150    205    175
'3/20/2010'        215    170    195    245    210
'3/20/2011'        275    215    240    285    265
'3/20/2012'        340    255    290    320    310



CVA Computation

The Credit Value (Valuation) Adjustment (CVA) formula is:

Where R is the recovery, discEE the discounted expected exposure at time t, and PD the default probability distribution. This assumes the exposure is independent of default (no wrong-way risk), and it also assumes the exposures were obtained using risk-neutral probabilities.

Here we approximate the integral with a finite sum over the valuation dates as:

where t_1 is todays date, t_2,...,t_n the future valuation dates.

We assume CDS info corresponds to counterparty with index cpIdx. The computed CVA is the present market value of our credit exposure to counterparty cpIdx. For this example we set the recovery rate at 40%.

Recovery = 0.4;
CVA = (1-Recovery) * sum(discEE(2:end,:) .* diff(DefProb));
for i = 1:numel(CVA)
fprintf('CVA for counterparty %d = $%.2f\n',i,CVA(i)); end figure; bar(CVA); title('CVA for each counterparty'); xlabel('Counterparty'); ylabel('CVA$');
grid on;

CVA for counterparty 1 = $2225.23 CVA for counterparty 2 =$2431.85
CVA for counterparty 3 = $929.50 CVA for counterparty 4 =$5064.17
CVA for counterparty 5 = \$5803.35


References

1. Pykhtin, Michael, and Steven Zhu, A Guide to Modelling Counterparty Credit Risk, GARP, July/August 2007, issue 37, pp. 16-22.

2. Pykhtin, Michael, and Dan Rosen, Pricing Counterparty Risk at the Trade Level and CVA, 2010.

3. Basel II: http://www.bis.org/publ/bcbs128.pdf page 256