Coding towards CFA (23) – Parallel Monte Carlo Simulations with DolphinDB

Coding towards CFA (23) – Parallel Monte Carlo Simulations with DolphinDB

*More articles can be found from my blog site - https://dataninjago.com

The Monte Carlo simulation method for pricing fixed-income instruments is introduced in CFA Level 2, Fixed Income, Module 2, Section 7. In this blog post, I will walk through the coding of the steps outlined in the CFA curriculum. The Monte Carlo approach can be computationally intensive, especially due to the need for simulating a large number of interest rate paths. As a result, the key to its practical application in real-world scenarios lies in computational efficiency. This is where DolphinDB excels. In this post, I will implement the Monte Carlo simulation using DolphinDB, leveraging its native parallel computing capabilities to enhance performance.

Two Theoretical Supports

The Monte Carlo approach for pricing fixed-income instruments relies on two key theoretical supports: pathwise valuation and interest rate structure modelling.

Pathwise valuation is a key concept behind the Monte Carlo simulation approach, particularly when valuing the path-dependent instruments. Pathwise valuation allows us to simulate multiple paths of interest rates over time, compute the corresponding cash flows, and then average these values to estimate the price of the instrument.

Interest rate structure modelling refers to the mathematical modelling of interest rates evolution over time, which can be used to simulate different interest rate paths required by the Monte Carlo approach. The interest rate structure modelling has been discussed in the previous blog post. In this blog post, we will use the Cox-Ingersoll-Ross (CIR) model as example.

Implement in DolphinDB

First, we create a function, simulate_CIR, to simulate the interest rate paths in CIR process. Further details of the CIR process can be found in the previous blog post.

We then create the simulate_per_path function that simulates one interest rate path and calculate the present value of the cash flows along this path.

We start by calling the simulate_CIR function to generate the interest rate path, which is stored in a list of interest rates at each time step. Next, we identify the time points of cash flow events (such as coupon or principal payments) along the interest rate path. We then discount the cash flows at these time points to their present values, resulting in the bond price based on this simulated interest rate path.

Once we have the function to calculate the bond price for a single simulated interest rate path, we can then call the function repeatedly to simulate enough paths to achieve acceptable accuracy, and calculate the bond price by averaging the prices from all paths.

Here, we use DolphinDB optimised ploop function for run the simulate_per_path function in parallel.

Full Code – DolphinDB

// Simulate the CIR process
def simulate_CIR(r0, rL, T, steps, sigma, kappa){
    dt = T / steps
    rates = []
    rates.append!(r0)
    dWs = randNormal(0, sqrt(dt), steps)
 
    for (i in 1..steps){
        dR = kappa * (rL - rates[i-1]) * dt + sigma * sqrt(rates[i-1]) * dWs[i]
        rates.append!(rates[i-1] + dR)
    }
    return rates
}
 
// price bond by simulating an interest path
def simulate_per_path(r0, rL, T, payment_freq, coupon_rate, face_value, 
                                            steps, sigma, kappa, num_simulation){
 
    // generate an interest rate path following CIR model
    rate_path = simulate_CIR(r0, rL, T, steps, sigma, kappa) 
     
    // locate the cashflow time points on the rate path
    cashflow_steps = steps / (T * payment_freq)
    cashflow_points = []
    for (i in 1..int(T * payment_freq)) {
        cashflow_points.append!(cashflow_steps * i)
    }
 
    // calculate the present value of the coupon payments
    coupon_value = coupon_rate * face_value
    dt = T /steps
    coupons = []
    for (p in cashflow_points){
        coupons.append!(coupon_value * exp(-rate_path[p]*(p*dt)))
    }
 
    // calculate the present value of the principal payments
    principal = face_value * exp(-rate_path[steps-1]*T)
 
    // calculate the bond price
    bond_price = sum(coupons) + principal
 
    return bond_price
}
 
// run the simulate_per_path funciton in parallel and return the results
// from all simulated paths
def run_simulations(r0, rL, T, payment_freq, coupon_rate, face_value, 
                                    steps, sigma, kappa, num_simulations){
 
        simulate = partial(simulate_per_path, r0, rL, T, payment_freq, 
                            coupon_rate, face_value, steps, sigma, kappa)
        prices = ploop(simulate, 1..num_simulations)
 
        bond_price = sum(prices) / count(prices)
     
        return bond_price
}
 
 
r0 = 0.05
rL = 0.05
kappa = 0.05
sigma = 0.05
T = 5.0
steps = 1000
num_simulations = 100
 
payment_freq = 2
face_value = 100
coupon_rate = 0.05
 
bond_prices =  run_simulations(r0, rL, T, payment_freq, coupon_rate,
                         face_value, steps, sigma, kappa, num_simulations)
 
print(bond_price)        

要查看或添加评论,请登录

Linxiao Ma的更多文章

社区洞察

其他会员也浏览了