Binomial tree calibrated to yield curve and volatility structure in C#

In case anyone finds it useful, here it is. I employed backward induction, which means O(N^2) calibration time, and my solver is disgustingly primitive, but it is simple and it works. And it uses O(N) memory.
Usecase is:

        public void Calculate() {
            var tree = new BinomialTree(
                new[] {0.0608, 0.0611, 0.0621, 0.0631, 0.0655}, 
                new[] {0.00, 0.17, 0.16, 0.15, 0.13});
            // now you can use tree.TreeRate(i,j) function to find rates in tree nodes
        }

And the code is:

public class Solver {
	public double? Solve(Func<double, double> what, double from, double to) {
		Debug.Assert(Math.Sign(what(from))!= Math.Sign(what(to)), "Function must be of different signs on borders");
		do {
			var mid = (to + from)/2;
			var valOnTo = what(to);
			var valOnMid = what(mid);
			if (Math.Sign(valOnTo) != Math.Sign(valOnMid)) 
				from = mid;
			else to = mid;
		} while (Math.Abs(from-to) > 10e-5);
		return (to + from) / 2;
	}
}

public class BinomialTree {
	private readonly double[] _treeRates;
	private readonly double[] _treeTerms;
	private readonly double[] _treeVols;
	private readonly int _n;

	/// <summary>
	/// Calculates interest rate in (i, j) node in tree
	/// </summary>
	/// <param name="i">number of time step</param>
	/// <param name="j">number of point by vertical axis, j = 0 is the highest, j = i is the lowest</param>
	/// <returns>Double value - an interest rate in (i, j) node in tree</returns>
	public double TreeRate(int i, int j) {
		Debug.Assert(i < _n && j < _n && j <= i, "Requested node is out of the tree");
		return _treeRates[i]*Math.Exp(-2*_treeVols[i]*j);
	}

	public BinomialTree(double[] rates, double[] vols, double[] terms = null) {
		Debug.Assert(rates.Length == vols.Length, "Count of rates and volatilities must be equal");

		_n = rates.Length;
		_treeRates = new double[_n];
		_treeVols = vols;
		if (terms != null) {
			Debug.Assert(terms.Length == vols.Length, "Count of rates and volatilities must be equal");
			_treeTerms = terms;
		} else {
			_treeTerms = new double[_n];
			for (var i = 0; i < _n; i++) _treeTerms[i] = i+1;
		}
		_treeRates[0] = rates[0];

		var solver = new Solver();
		for (var i = 1; i < _n; i++) {
			// 1) calculating price of ZCB with term i and rate r[i]
			var price = Math.Exp(-rates[i]*_treeTerms[i]);

			var num = i; // to avoid using cycle variable in clojure

			// 2) finding rate which would make bond price equal to calculated price
			var rt = solver.Solve(rate => EvaluatePrice(rate, num + 1) - price, 0.00, 1.00);
			if (rt == null) throw new InvalidOperationException("Didn't converge!");

			// 3) Saving result
			_treeRates[i] = rt.Value;
		}
	}

	private double EvaluatePrice(double rate, int num) {
		var oldPrices = new double[num + 1];
		var newPrices = new double[num];
		for (var i = 0; i < num + 1; i++) oldPrices[i] = 1;

		for (var n = num-1; n >= 0; n--) {
			for (var k = 0; k <= n; k++) {
				var rt = ((n == num - 1) ? rate : _treeRates[n]) * Math.Exp(-2 * _treeVols[n] * k);
				newPrices[n - k] = 0.5*(oldPrices[n-k] + oldPrices[n-k+1])*Math.Exp(-rt);
			}
			if (n > 0) for (var k = 0; k <= n; k++) oldPrices[k] = newPrices[k];
		}
		return newPrices[0];
	}
}
About these ads

Leave a comment

Filed under .net, Interest rate models, Quant finance

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s