BlackDermanToyYieldOnlyInterestRateModel { public Function1D<StandardDiscountBondModelDataBundle, RecombiningBinomialTree<Triple<Double, Double, Double>>> getTrees(final ZonedDateTime time) { Validate.notNull(time, "time"); return new Function1D<StandardDiscountBondModelDataBundle, RecombiningBinomialTree<Triple<Double, Double, Double>>>() { @SuppressWarnings({"unchecked", "synthetic-access" }) @Override public RecombiningBinomialTree<Triple<Double, Double, Double>> evaluate(final StandardDiscountBondModelDataBundle data) { Validate.notNull(data, "data"); final double[][] r = new double[_n + 1][_j]; final double[][] q = new double[_n + 1][_j]; final double[][] d = new double[_n + 1][_j]; final double[] u = new double[_n + 1]; final double[] p = new double[_n + 2]; final double t = DateUtils.getDifferenceInYears(data.getDate(), time); final double dt = t / _n; final double dtSqrt = Math.sqrt(dt); final double r1 = data.getShortRate(dt); final double sigma = data.getShortRateVolatility(dt); p[0] = 1.0; for (int i = 1; i <= _n + 1; i++) { p[i] = 1. / Math.pow((1 + data.getShortRate(i) * dt), dt * i); } q[0][0] = 1.; u[0] = r1; r[0][0] = r1; d[0][0] = 1. / (1 + r1 * dt); for (int i = 1; i <= _n; i++) { q[i][0] = 0.5 * q[i - 1][0] * d[i - 1][0]; q[i][i] = 0.5 * q[i - 1][i - 1] * d[i - 1][i - 1]; for (int j = -i + 2, k = 1; j <= i - 2; j += 2, k++) { q[i][k] = 0.5 * (q[i - 1][k - 1] * d[i - 1][k - 1] + q[i - 1][k] * d[i - 1][k]); } u[i] = _rootFinder.getRoot(getMedian(sigma, i, dt, q, p[i + 1]), 0., 1.); for (int j = -i, k = 0; j <= i; j += 2, k++) { r[i][k] = u[i] * Math.exp(sigma * j * dtSqrt); d[i][k] = 1. / (1 + r[i][k] * dt); } } final Triple<Double, Double, Double>[][] result = new Triple[_n + 1][_j]; for (int i = 0; i <= _n; i++) { for (int j = 0; j < _j; j++) { result[i][j] = new Triple<>(r[i][j], d[i][j], q[i][j]); } } return new RecombiningBinomialTree<>(result); } }; } BlackDermanToyYieldOnlyInterestRateModel(final int n); Function1D<StandardDiscountBondModelDataBundle, RecombiningBinomialTree<Triple<Double, Double, Double>>> getTrees(final ZonedDateTime time);  }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullTime() { new BlackDermanToyYieldOnlyInterestRateModel(3).getTrees(null); }
@Test(expectedExceptions = IllegalArgumentException.class) public void testNullData() { new BlackDermanToyYieldOnlyInterestRateModel(5).getTrees(DateUtils.getUTCDate(2010, 8, 1)).evaluate((StandardDiscountBondModelDataBundle) null); }
@Test public void test() { final int steps = 3; final ZonedDateTime date = DateUtils.getUTCDate(2009, 1, 1); final ZonedDateTime maturity = DateUtils.getDateOffsetWithYearFraction(date, 3); final StandardDiscountBondModelDataBundle data = new StandardDiscountBondModelDataBundle(YieldCurve.from(ConstantDoublesCurve.from(0.05)), new VolatilityCurve(ConstantDoublesCurve.from(0.1)), date); final BlackDermanToyYieldOnlyInterestRateModel model = new BlackDermanToyYieldOnlyInterestRateModel(steps); final RecombiningBinomialTree<Triple<Double, Double, Double>> tree = model.getTrees(maturity).evaluate(data); final Triple<Double, Double, Double>[][] result = tree.getNodes(); @SuppressWarnings("unchecked") final Triple<Double, Double, Double>[][] expected = new Triple[4][4]; expected[0][0] = new Triple<>(0.05, 0.9524, 1.0); expected[1][0] = new Triple<>(0.045, 0.9569, 0.4762); expected[1][1] = new Triple<>(0.055, 0.9479, 0.4762); expected[2][0] = new Triple<>(0.0406, 0.9610, 0.2278); expected[2][1] = new Triple<>(0.0496, 0.9528, 0.4535); expected[2][2] = new Triple<>(0.0605, 0.9430, 0.2257); expected[3][0] = new Triple<>(0.0366, 0.9647, 0.1095); expected[3][1] = new Triple<>(0.0447, 0.9572, 0.3255); expected[3][2] = new Triple<>(0.0546, 0.9482, 0.3224); expected[3][3] = new Triple<>(0.0667, 0.9375, 0.1064); assertEquals(result.length, expected.length); assertEquals(result[0].length, expected[0].length); for (int i = 0; i < expected.length; i++) { for (int j = 0; j < expected[0].length; j++) { if (expected[i][j] == null) { final Triple<Double, Double, Double> triple = result[i][j]; assertEquals(triple.getFirst(), 0, 1e-16); assertEquals(triple.getSecond(), 0, 1e-16); assertEquals(triple.getThird(), 0, 1e-16); } else { final Triple<Double, Double, Double> triple1 = result[i][j]; final Triple<Double, Double, Double> triple2 = expected[i][j]; assertEquals(triple1.getFirst(), triple2.getFirst(), 1e-4); assertEquals(triple1.getSecond(), triple2.getSecond(), 1e-4); assertEquals(triple1.getThird(), triple2.getThird(), 1e-4); } } } }