Life goes on and very fortunately there are a lot of other alternatives available. This time I wanted to present Alglib, which is offering quite impressive package of numerical stuff already in their non-commercial (free of charge) edition. In optimization category, there is Levenberg-Marquardt algorithm (LMA) implementation available, which can be used for multivariate optimization tasks. Within this post, I am re-publishing short interest rate model calibration scheme, but only using Alglib LMA for performing the actual optimization routine.

In the case that Alglib is completely new package, it is recommended to check out some library documentation first, since it contains a lot of simple "Copy-Paste-Run"-type of examples, which will help to understand how the flow of information is processed. As a personal note I have to say, that time and effort needed to "getting it up and running" is extremely low. Just find Alglib download page, download proper C# version, create a new C# project and add reference to DLL file (alglibnet2.dll) included in download folder.

## The outcome

Market prices of zero-coupon bonds, volatility (assumed to be estimated constant) and short rate are used, in order to solve time-dependent theta coefficients for Ho-Lee short interest rate model. When these coefficients have been solved, the model can be used to price different types of interest rate products. The original data and other necessary details have been already presented in here.

Alglib LMA package offers a lot of flexibility, concerning how the actual optimization task will be solved. Basically, there are three different schemes : V (using function vector only), VJ (using function vector and first order partial derivatives, known as Jacobian matrix) and FGH (using function vector, gradient and second order partial derivatives, known as Hessian matrix). Solved time-dependent theta coefficients for the first two schemes (V, VJ) are presented in the screenshot below.

Due to the usage of analytical first order partial derivatives instead of numerical equivalents (finite difference), Vector-Jacobian scheme (VJ) is a bit more accurate and computationally much faster. For calculating required partial derivatives for Jacobian/Hessian matrix, one might find this online tool quite handy.

## The program

In order to test this calibration program, just create a new C# project (AlgLibTester), copy-paste the following code into a new CS file and add reference to Alglib DLL file.

A few words on this program. First of all, Alglib LMA has been wrapped inside static LevenbergMarquardtSolver class, in order to provide kind of a generic way to use that solver. Basically, LMA requires callback method(s) for calculating values for vector of functions and/or Jacobian matrix. In this program, these callback methods are first defined as interfaces, which (the both in this program) are going to be implemented in HoLeeZeroCouponCalibration class. This class is also storing all the "source data" (maturities, zero-coupon bond prices, volatility and short rate) to be used, as LMA is processing its data (coefficients to be solved) through these callback methods. Note, that LevenbergMarquardtSolver class has absolutely no information about external world, except that it can receive callback methods defined in IFunctionVector and IJacobianMatrix interfaces. With this scheme, we can separate all financial algorithms and data from the actual optimization task.

using System; using System.Linq; // // 'type definitions' for making life a bit more easier with long class names using LM = AlgLibTester.LevenbergMarquardtSolver; using HoLee = AlgLibTester.HoLeeZeroCouponCalibration; namespace AlgLibTester { class Program { static void Main(string[] args) { // short rate volatility, short rate, vector of maturities, vector of zero-coupon bond prices double sigma = 0.00039; double r0 = 0.00154; double[] t = new double[] { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0 }; double[] zero = new double[] { 0.9964, 0.9838, 0.9611, 0.9344, 0.9059, 0.8769, 0.8478, 0.8189, 0.7905, 0.7626 }; // // create callback functions wrapped inside a class, which // implements IFunctionVector and IJacobianMatrix interfaces HoLee callback = new HoLee(sigma, r0, t, zero); // // container for initial guesses for theta coefficients, which are going to be solved double[] theta = null; // // Example 1 : // use function vector only (using 'V' mode of the Levenberg-Marquardt optimizer) theta = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; LM.Solve(ref theta, 10, (IFunctionVector)callback); Console.WriteLine("Using function vectors only"); Console.WriteLine("iterations : {0}", LM.report.iterationscount); theta.ToList<double>().ForEach(it => Console.WriteLine(it)); Console.WriteLine(""); // // Example 2 : // use function vector and jacobian matrix (using 'VJ' mode of the Levenberg-Marquardt optimizer) theta = new double[] { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; LM.Solve(ref theta, 10, (IFunctionVector)callback, (IJacobianMatrix)callback); Console.WriteLine("Using function vectors and Jacobian matrix"); Console.WriteLine("iterations : {0}", LM.report.iterationscount); theta.ToList<double>().ForEach(it => Console.WriteLine(it)); } } // // // // static class, which is wrapper for Alglib Levenberg-Marquardt solver // in order to make the usage of this particular solver a bit more 'generic' public static class LevenbergMarquardtSolver { // alglib report is exposed to public public static alglib.minlmreport report; private static alglib.minlmstate state; // // create minimization solver using only function vector ('V' vector method) public static void Solve(ref double[] x, int m, IFunctionVector functionVector, // optional parameters which are set to default values double diffstep = 1E-08, double epsg = 1E-15, double epsf = 0.0, double epsx = 0.0, int maxits = 0) { // create LM model, set termination conditions, perform optimization and get results alglib.minlmcreatev(m, x, diffstep, out state); alglib.minlmsetcond(state, epsg, epsf, epsx, maxits); alglib.minlmoptimize(state, functionVector.callback, null, null); alglib.minlmresults(state, out x, out report); } // method overloading : create minimization solver using function vector and Jacobian matrix ('VJ' vector-jacobian method) public static void Solve(ref double[] x, int m, IFunctionVector functionVector, IJacobianMatrix jacobianMatrix, // optional parameters which are set to default values double epsg = 1E-15, double epsf = 0.0, double epsx = 0.0, int maxits = 0) { // create LM model, set termination conditions, perform optimization and get results alglib.minlmcreatevj(m, x, out state); alglib.minlmsetcond(state, epsg, epsf, epsx, maxits); alglib.minlmoptimize(state, functionVector.callback, jacobianMatrix.callback, null, null); alglib.minlmresults(state, out x, out report); } } // // // // interface : definition for function vector callback method, required by LevenbergMarquardtSolver class public interface IFunctionVector { void callback(double[] x, double[] fi, object obj); } // // // // interface : definition for jacobian matrix callback method, optionally required by LevenbergMarquardtSolver class public interface IJacobianMatrix { void callback(double[] x, double[] fi, double[,] jac, object obj); } // // // // Ho-Lee calibration class, which implements IFunctionVector and IJacobianMatrix interfaces public class HoLeeZeroCouponCalibration : IFunctionVector, IJacobianMatrix { // parameters, which are required in order to calculate // zero-coupon bond prices using Ho-Lee short rate model private double sigma; private double r0; private double[] t; private double[] zero; // public HoLeeZeroCouponCalibration(double sigma, double r0, double[] t, double[] zero) { this.sigma = sigma; this.r0 = r0; this.t = t; this.zero = zero; } // callback method for calculating vector of values for functions void IFunctionVector.callback(double[] x, double[] fi, object obj) { // calculate squared differences of Ho-Lee prices (using theta coefficients) // and market prices and then assign these differences into function vector fi for (int i = 0; i < fi.Count(); i++) { double HOLEE_ZERO = Math.Exp(-Math.Pow(t[i], 2.0) * x[i] / 2.0 + Math.Pow(sigma, 2.0) * Math.Pow(t[i], 3.0) / 6.0 - r0 * t[i]); fi[i] = Math.Pow(zero[i] - HOLEE_ZERO, 2.0); } } // callback method for calculating partial derivatives for jacobian matrix void IJacobianMatrix.callback(double[] x, double[] fi, double[,] jac, object obj) { double HOLEE_ZERO = 0.0; // part 1 : calculate squared differences of Ho-Lee prices (using theta coefficients) // and market prices, then assign these squared differences into function vector fi for (int i = 0; i < fi.Count(); i++) { HOLEE_ZERO = Math.Exp(-Math.Pow(t[i], 2.0) * x[i] / 2.0 + Math.Pow(sigma, 2.0) * Math.Pow(t[i], 3.0) / 6.0 - r0 * t[i]); fi[i] = Math.Pow(zero[i] - HOLEE_ZERO, 2.0); } // part 2 : calculate all partial derivatives for Jacobian square matrix // loop through m functions for (int i = 0; i < fi.Count(); i++) { // loop through n theta coefficients for (int j = 0; j < x.Count(); j++) { double derivative = 0.0; // partial derivative is non-zero only for diagonal cases if (i == j) { HOLEE_ZERO = Math.Exp(-Math.Pow(t[j], 2.0) * x[j] / 2.0 + Math.Pow(sigma, 2.0) * Math.Pow(t[j], 3.0) / 6.0 - r0 * t[j]); derivative = Math.Pow(t[j], 2.0) * (zero[j] - HOLEE_ZERO) * HOLEE_ZERO; } jac[i, j] = derivative; } } } } }

Finally, thanks for reading my blog.

-Mike