public void testZeroBondPricing() { // Testing Monte-Carlo zero bond pricing DayCounter dc = new Actual360(); Date today = Date.Today; Settings.Instance.setEvaluationDate(today); // construct a strange yield curve to check drifts and discounting // of the joint stochastic process List <Date> dates = new List <Date>(); List <double> times = new List <double>(); List <double> rates = new List <double>(); dates.Add(today); rates.Add(0.02); times.Add(0.0); for (int i = 120; i < 240; ++i) { dates.Add(today + new Period(i, TimeUnit.Months)); rates.Add(0.02 + 0.0002 * Math.Exp(Math.Sin(i / 8.0))); times.Add(dc.yearFraction(today, dates.Last())); } Date maturity = dates.Last() + new Period(10, TimeUnit.Years); dates.Add(maturity); rates.Add(0.04); //times.Add(dc.yearFraction(today, dates.Last())); Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); Handle <YieldTermStructure> ts = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> ds = new Handle <YieldTermStructure>(Utilities.flatRate(today, 0.0, dc)); HestonProcess hestonProcess = new HestonProcess(ts, ds, s0, 0.02, 1.0, 0.2, 0.5, -0.8); HullWhiteForwardProcess hwProcess = new HullWhiteForwardProcess(ts, 0.05, 0.05); hwProcess.setForwardMeasureTime(dc.yearFraction(today, maturity)); HullWhite hwModel = new HullWhite(ts, 0.05, 0.05); HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwProcess, -0.4); TimeGrid grid = new TimeGrid(times); int factors = jointProcess.factors(); int steps = grid.size() - 1; SobolBrownianBridgeRsg rsg = new SobolBrownianBridgeRsg(factors, steps); MultiPathGenerator <SobolBrownianBridgeRsg> generator = new MultiPathGenerator <SobolBrownianBridgeRsg>( jointProcess, grid, rsg, false); int m = 90; List <GeneralStatistics> zeroStat = new InitializedList <GeneralStatistics>(m); List <GeneralStatistics> optionStat = new InitializedList <GeneralStatistics>(m); int nrTrails = 8191; int optionTenor = 24; double strike = 0.5; for (int i = 0; i < nrTrails; ++i) { Sample <IPath> path = generator.next(); MultiPath value = path.value as MultiPath; Utils.QL_REQUIRE(value != null, () => "Invalid Path"); for (int j = 1; j < m; ++j) { double t = grid[j]; // zero end and option maturity double T = grid[j + optionTenor]; // maturity of zero bond // of option Vector states = new Vector(3); Vector optionStates = new Vector(3); for (int k = 0; k < jointProcess.size(); ++k) { states[k] = value[k][j]; optionStates[k] = value[k][j + optionTenor]; } double zeroBond = 1.0 / jointProcess.numeraire(t, states); double zeroOption = zeroBond * Math.Max(0.0, hwModel.discountBond(t, T, states[2]) - strike); zeroStat[j].add(zeroBond); optionStat[j].add(zeroOption); } } for (int j = 1; j < m; ++j) { double t = grid[j]; double calculated = zeroStat[j].mean(); double expected = ts.link.discount(t); if (Math.Abs(calculated - expected) > 0.03) { QAssert.Fail("Failed to reproduce expected zero bond prices" + "\n t: " + t + "\n calculated: " + calculated + "\n expected: " + expected); } double T = grid[j + optionTenor]; calculated = optionStat[j].mean(); expected = hwModel.discountBondOption(Option.Type.Call, strike, t, T); if (Math.Abs(calculated - expected) > 0.0035) { QAssert.Fail("Failed to reproduce expected zero bond option prices" + "\n t: " + t + "\n T: " + T + "\n calculated: " + calculated + "\n expected: " + expected); } } }