1
$\begingroup$

I have simulated some data according to a Vasicek process and I am then trying to apply ordinary least squares (OLS) regression analysis to see how accurate the estimated model parameters are from the estimation. However, my estimated model parameters do not even seem close to what they should be. I have tried letting T get very large, and I have even simulated 100,000 paths and estimated the model for each path, and then averaged the estimates. Yet the results still look awry. So either I am simulating my data wrong, or the regression I am doing is somehow wrong. I have attached my python code below, so if anybody can take a look and let me know why my model parameters are off I would appreciate it very much.

The form of the model I am using is:

$$ dr_t = (a - b r_t) dt + \sigma dW.$$

To estimate my model parameters I am fitting a regression on the discrete data that gives me the following model:

$$ r_{t+1} - r_{t} = (a - b r_t) \Delta t + \sigma Z_t$$

where $ Z_t \sim N(0,\Delta t)$ (i.e. mean 0, variance $\Delta t$). From this setup I know that my estimated intercept will be $ a\Delta t$ and my estimated slope will be $-1 * b \Delta t$. Therefore, in my fit_vasicek_model function, I make the necessary transformation to recover "a" and "b".

Here is my code ( I have omitted some portions that are irrelevant to this question )

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import random
import statsmodels.formula.api as smf
import statsmodels.api as sm
import statsmodels.tools as sm_tools


class InterestRateModels:
    
    def __init__(self, nsim, T, Vas = None):
        self.N = nsim
        self.T = T
        self.dt = 1/T
        self.Vas = Vas
    
    def sim_vasicek(self, r0, a, b, sigma):
        """
        Inputs:
        -------
        N = number of paths to simulate
        T = number of time steps to take
        r0 = initial (current) short-term rate
        a, b, sigma = parameters of the model
        
        The Model:
        ----------
        dr_t = (a − b*r_t)*dt + σ * dBt
        
        r_t = short rate
        a = long-run average rate, a in Real Numbers
        b = speed of reversion, b > 0
        sigma = volatility of short rate
        Bt = Gaussian (0 mean, 1 sigma) process
        
        Returns:
        --------
        Creates an object attribute that is a matrix of simulated short rate paths.
        """
        dt = 1.0/self.T
        X = np.random.normal(0,np.sqrt(dt),size=(self.N,self.T))
        self.Vas = np.zeros((self.N,self.T)); self.Vas[:,0] = r0;
        for i in range(self.N):
            for j in range(1,self.T):
                self.Vas[i,j] = self.Vas[i,j-1] + (a-b*self.Vas[i,j-1]) * dt + sigma * X[i,j]
        return
                
    def plot_vasicek(self):
        for i in range(self.N):
            plt.plot(self.Vas[i,:])
        return
    
    def fit_vasicek_model(self, rate_path):
        """
        Inputs:
        -------
        rate_path is a single interest rate path.
        
        Returns:
        --------
        Parameters of the model (not the regression paraneters, as they get transformed)
        Intercept = a * dt
        Slope = -1 * b * dt (The slope needs to be multiplied by negative 1)
        """

        n = len(rate_path)
        X = rate_path[:n-1]
        y = np.diff(rate_path)
        X = sm_tools.tools.add_constant(X)
        model = sm.OLS(y,X)
        results = model.fit()
        a, b1 = results.params / self.dt
        b = -1 * b1
        return a, b

So now when I create an instance and simulate a bunch of paths, then fit the model I parameters for a and b that are not even close to their true value. Here is my guess as to why: There needs to be a long enough of a period (i.e, T is large enough), relative to the reversion parameter, b. Meaning if b is large, T can be small. If b is small, T must be large.

# nsim=1000, T=30, a=1, b=4 #
rates_sim = InterestRateModels(nsim=1000, T=30)
rates_sim.sim_vasicek(.02, a=1, b=4, sigma=.3)
rates_sim.plot_vasicek()

b_lst = []
a_lst = []
for i in range(rates_sim.N):
    rate_path = rates_sim.Vas[i,:]
    a, b = rates_sim.fit_vasicek_model(rate_path)
    a_lst.append(a)
    b_lst.append(b)

np.mean(b_lst)
Out[273]: 7.0396842314975165

Notice that the mean of b is 7.03, not 4. So now when I let T=1000 this is what I see.

np.mean(b_lst)
Out[275]: 7.538313498938557

This is still a bit off. I did look at the plot of the rate curves and they don't totally look stationary. So letting b get really big (i.e. b=30), even for a smaller T=30 I get the following result

np.mean(b_lst)
Out[277]: 31.078385891056488

This looks like what I would expect. So, what is the general rule for reasonable convergence? I have read somewhere that we must have $\mid 1 - b \Delta t \mid <= 1$, though when $\Delta t = 1/30$ and $b=4$, this still holds true, yet the results look awful. Thanks for reading this, and I appreciate any help you filks might give.

$\endgroup$
1
  • $\begingroup$ Your SDE simulation is using Euler approximation, but with Vasicek we have the exact terminal distribution available. You might as well use that. Or at least use Milstein. I'm not saying the simulation accuracy is at fault here for sure, but with such unusual parameter values it might well be. $\endgroup$
    – Brian B
    Jun 20 at 13:22

2 Answers 2

1
$\begingroup$

Your model is:

dr_t = (a − b*r_t)*dt + σ * dBt

However, the Vasicek model is:

dr_t = a(b - r_t)*dt + σ * dBt

Can you update the equation and try again?

Typically, a is the mean reversion speed and b is the mean reversion level

https://en.wikipedia.org/wiki/Vasicek_model

$\endgroup$
2
  • $\begingroup$ Thanks, I will try this. Although, in my parameterization the mean is b/a, so my initial guess is that by changing how I simulate my data I will just be changing the interpretation of the regression parameters. $\endgroup$ May 31, 2021 at 20:51
  • $\begingroup$ Did this end up helping? $\endgroup$
    – VVKK77
    Jun 29, 2021 at 22:57
1
$\begingroup$

Check out this article: https://papers.ssrn.com/sol3/papers.cfm?abstract_id=130068

This is done in discrete time which makes it more friendly to calibrate in practice.

$\endgroup$
1
  • $\begingroup$ I will read this and see if it helps. Thanks. $\endgroup$ May 31, 2021 at 20:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.