-
Basics Part 1 2
-
Lecture1.1
-
Lecture1.2
-
-
Basics Part 2 1
-
Lecture2.1
-
-
Credit Migration 2
-
Lecture3.1
-
Lecture3.2
-
-
Vasicek Model 2
-
Lecture4.1
-
Lecture4.2
-
-
Payments 2
-
Lecture5.1
-
Lecture5.2
-
-
Complete System 2
-
Lecture6.1
-
Lecture6.2
-
Basic Payments
This part of the course will be around the formulation of the payments part of the simulation. There is no randomness in this module, however, in the lesson after it the results will be different at the end because bonds may potentially have defaulted and as well we will differing interest rates.
The initial portfolio consists of a few columns. We define the rating, the coupon rate (what the bond pays over a year as a percent of face value), the face value which is the payment at the end of the bond maturity and the payment frequency (6 denotes a bond that pays a coupon every 6 months, 12 denotes one that pays a coupon every 12 months).
import pandas as pd
import matplotlib.pyplot as plt
from scipy.stats import norm
import numpy as np
import pandas as pd
from cadCAD.configuration.utils import config_sim
from cadCAD.configuration import Experiment
from cadCAD.engine import ExecutionMode, ExecutionContext, Executor
from cadCAD import configs
#Create the portfolio
portfolio = pd.DataFrame(["AAA"]*10+["AA"]*10+["A"]*10, columns = ['Rating'])
portfolio['Coupon Rate'] = [.08] * 10 + [.09] * 10 + [.1] * 10
portfolio['Payment Frequency'] = [6,12] * 15
portfolio['Face Value'] = 1000
print(portfolio)
#Set initial portfolio
initial_state = {
'portfolio': portfolio,
'default': 0,
'cash': 0,
'payment': 0,
'r': .06
}
The s variable for the simulation has a timestep component that we can use. It starts at 0 and goes up by 1 each timestep. Using the modulo operator (%) we get to see what the remainder of one number divided by the other is. We will say that if the timestep plus 1 modulo the payment frequency is 0, then a coupon payment is paid that period. We get a 1 or 0 to denote this then we multiply that by the coupon rate times the face value times 12 divided by the payment frequency (so that if we have, for example, a payment period that is 6, the coupon is only half since we get two payments a year).
We also need to set bond payments where the bond rating is default to 0 which we can do by multiplying in (portfolio[‘Rating’] != ‘Default’).astype(int) to the payment. The code for the functions is below.
#Function to compute bond payments
def bond_payments(_params, substep, sH, s):
#Compute coupon payments
payment = (((s['timestep']+1) % portfolio['Payment Frequency']) == 0).astype(int) * portfolio['Coupon Rate'] * portfolio['Face Value'] * portfolio['Payment Frequency'] / 12
#If the timestep is equal to 360, add the face value
payment = payment + portfolio['Face Value'] * ((s['timestep'] + 1) % 360 == 0)
#Set payments for defaulted bonds equal to 0
payment = payment * (portfolio['Rating'] != 'Default').astype(int)
payment = payment.sum()
return ({"payment": payment})
#Update the payment
def update_payments(params, step, sL, s, _input):
return ('payment', _input['payment'])
Run the simulation now.
#Partial updates
PSUBs = [
{
"policies": {
"payment_policy": bond_payments,
},
"variables": {
"payment": update_payments,
}
}
]
#Set simulation parameters
#The M parameter will let us test different combinations of parameters
sim_config_dict = {
'T': range(360),
'N': 5}
c = config_sim(sim_config_dict)
exp = Experiment()
exp.append_configs(
initial_state = initial_state,
partial_state_update_blocks = PSUBs,
sim_configs = c
)
exec_mode = ExecutionMode()
local_mode_ctx = ExecutionContext(context=exec_mode.single_mode)
simulation = Executor(exec_context=local_mode_ctx, configs=configs)
raw_result, field, sessions = simulation.execute()
result = pd.DataFrame(raw_result)
Let’s graph the first 36 payments and the last 36 payments as a sanity check. We should see that every 6 months has a payment and every 12 months has a larger payment. As well, we will see how much larger the payment is on the last day of the bonds’ life.
payments = result.groupby('timestep')['payment'].mean()
payments.iloc[:37].plot(kind='bar')
plt.title("First 36 Months Payments")
plt.ylabel("Payment")
plt.show()
payments.iloc[-37:].plot(kind='bar')
plt.title("Last 36 Months Payments")
plt.ylabel("Payment")
plt.show()