Coding towards CFA (30) – Calculating CVA (Credit Valuation Adjustment)
Linxiao Ma
Financial Data Architect | Hands-on Engineer | PhD | CFA Candidate | Distributed Database Expert | DolphinDB UK Rep. | Tech Blogger | Insane Coder
*More articles can be found from my blog post - https://dataninjago.com
Credit Valuation Adjustment (CVA) is the present value of the potential credit risk associated with an investment that carries counterparty risk. The CFA curriculum, Fixed Income, Module 4, Section 2, presents the detailed steps for calculating CVA. In this blog post, I will replicate these steps using Python. Below is a diagram I created to illustrate these steps.
First, we define the characteristics of a sample bond for our exercise, including the bond’s terms, as well as the credit spread and recovery rate, which are assumed to be observed from the bond issuer.
Based on the bond’s investment horizon and payment frequency, we calculated the payment schedule and the corresponding discount factors for each payment time point.
The calculation of CVA (Credit Valuation Adjustment) depends on three components: the Expected Exposure, the Loss Given Default (LGD), and the Probability of Default (POD).
The key component, the Probability of Default, is determined by the hazard rate and the probability of survival, which can be derived from the credit spread and recovery rate:
Hazard Rate
hazard rate is the conditional probability of default, i.e., the probability that an event will occur given that it has not already occurred. The hazard rate can be derived from Credit Spread and Recovery Rate, i.e., Credit Spread divided by Loss Given Default (1 – Recovery Rate).
Probability of Survival
The survival possibility quantifies the likelihood that a reference entity will not default over a given time horizon, which can be calculated with the following formula where?λt?part represents the total risk of default over the time t and the the negative of that (-?λt) represents the probability of survival decays over time.
领英推荐
Probability of Default
The probability of default can be calculated by multiplying the Hazard Rate by the Probability of Survival of the previous period.
Below is the Python code I created to perform the calculations described above.
The Expected Exposure at a specific time is the present value of the notional amount at that time, which can be calculated by multiplying the notional amount of the bond by the discount factor at that time.
The Loss Given Default can be simply calculated as (1 – recovery rate). Using the Probability of Default, Expected Exposure, and Loss Given Default, we can determine the Expected Loss at a specific time. The present value of the expected loss is then obtained by multiplying it by the discount factor.
The CVA (Credit Valuation Adjustment) can be calculated by summing the present values of the expected losses across all payment time points.
Based on the calculated CVA, we can further derive the bond yield, taking into account the credit risk and the credit spread. Below is a diagram I created to visualise the calculation process.
Here is the python code for the calculations.
Full Code – Python
import numpy as np
# Observed data
cds_spread = 0.0075
risk_free_rate = 0.03
recovery_rate = 0.40
# Contract terms
notional = 1000000
T = 5
payment_freq = 1
coupon_rate = 0
# Generate the list of payment time points in the unit of year(s),
# e.g., 0.5 represents half year
payment_schedule = [(1/payment_freq) * (n+1)
for n in list(range(T * payment_freq))]
print(f"Payment Schedule: {payment_schedule}")
# Calculate the list of discount factors for each payment time points
df = [1/(1+risk_free_rate)**t
for t in payment_schedule]
print(f"Discount Factors: {df}")
# Calculate the hazard rate based on the observed cds spread
# and recovery rate of the issuer
hazard_rate = cds_spread / (1 - recovery_rate)
print(f"Hazard rate: {hazard_rate}")
# Calculate the probability of survive (POS) based on the hazard rate
probability_of_survive = np.array(
[(1-hazard_rate)**t
for t in payment_schedule]
)
print(f"POS: {hazard_rate}")
# Calculate the probability of default (POD)
# as POS (of previous period) * hazard_rate
probability_of_default = np.array(
(
[hazard_rate]
+
[pos * hazard_rate for pos in probability_of_survive]
)[0:-1]
)
print(f"POD: {probability_of_default}")
# Calculate present value of notional exposures at the payment time points
expected_exposure = np.array(
[notional/(1+risk_free_rate) ** (len(payment_schedule)-t)
for t in payment_schedule]
)
print(f"Expected Exposure:{expected_exposure}")
# Calculate expected loss
loss_give_default = 1 - recovery_rate
recoverable = expected_exposure * recovery_rate
expected_loss = expected_exposure * loss_give_default * probability_of_default
print(f"LGD: {loss_give_default}")
print(f"Recoverables: {recoverable}")
print(f"Expected Loss: {expected_loss}")
# Calculate the present value of expected losses
pv_expected_loss = expected_loss * df
print(f"PV of Expected Loss: {pv_expected_loss}")
# Calculate the CVA by summing up the present value of
# expected losses at the payment time points
CVA = sum(pv_expected_loss)
print(f"CVA: {CVA}")
# Calucate the price of the underlying bond without considering the credit risks
risk_free_price = notional / (1+risk_free_rate) ** T
print(f"Risk Free Price: {risk_free_price}")
# Calcualte the fair value of the underlying bond with considering the credit risks
fair_value = risk_free_price - CVA
print(f"Fair Value: {fair_value}")
# Derive the fair yield from the fair value
fair_yield = (notional / fair_value) ** (1/T) - 1
print(f"Fair Yield: {fair_yield}")
# Calculate hte credit spread as the difference between fair yield and risk free rate
credit_spread = (fair_yield - risk_free_rate) * 10000
print(f"Credit Spread: {round(credit_spread, 2)}")