public void testDiscretizationError() { // Testing the discretization error of the Heston Hull-White process 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>(), divRates = new List <double>(); for (int i = 0; i <= 31; ++i) { dates.Add(today + new Period(i, TimeUnit.Years)); // FLOATING_POINT_EXCEPTION rates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i))); divRates.Add(0.04 + 0.0001 * Math.Exp(Math.Sin(i))); times.Add(dc.yearFraction(today, dates.Last())); } Date maturity = today + new Period(10, TimeUnit.Years); double v = 0.25; Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); SimpleQuote vol = new SimpleQuote(v); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc)); BlackScholesMertonProcess bsmProcess = new BlackScholesMertonProcess(s0, qTS, rTS, volTS); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v * v, 1, v * v, 1e-6, -0.4); HullWhiteForwardProcess hwProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01); hwProcess.setForwardMeasureTime(20.1472222222222222); double tol = 0.05; double[] corr = { -0.85, 0.5 }; double[] strike = { 50, 100, 125 }; for (int i = 0; i < corr.Length; ++i) { for (int j = 0; j < strike.Length; ++j) { StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Put, strike[j]); Exercise exercise = new EuropeanExercise(maturity); VanillaOption optionBsmHW = new VanillaOption(payoff, exercise); HullWhite hwModel = new HullWhite(rTS, hwProcess.a(), hwProcess.sigma()); optionBsmHW.setPricingEngine(new AnalyticBSMHullWhiteEngine(corr[i], bsmProcess, hwModel)); double expected = optionBsmHW.NPV(); VanillaOption optionHestonHW = new VanillaOption(payoff, exercise); HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwProcess, corr[i]); optionHestonHW.setPricingEngine( new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess) .withSteps(1) .withAntitheticVariate() .withAbsoluteTolerance(tol) .withSeed(42).getAsPricingEngine()); double calculated = optionHestonHW.NPV(); double error = optionHestonHW.errorEstimate(); if ((Math.Abs(calculated - expected) > 3 * error && Math.Abs(calculated - expected) > 1e-5)) { QAssert.Fail("Failed to reproduce discretization error" + "\n corr: " + corr[i] + "\n strike: " + strike[j] + "\n calculated: " + calculated + "\n error: " + error + "\n expected: " + expected); } } } }
public void testAnalyticHestonHullWhitePricing() { // Testing analytic Heston Hull-White option 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>(), divRates = new List <double>(); for (int i = 0; i <= 40; ++i) { dates.Add(today + new Period(i, TimeUnit.Years)); // FLOATING_POINT_EXCEPTION rates.Add(0.03 + 0.0001 * Math.Exp(Math.Sin(i / 4.0))); divRates.Add(0.02 + 0.0002 * Math.Exp(Math.Sin(i / 3.0))); times.Add(dc.yearFraction(today, dates.Last())); } Date maturity = today + new Period(5, TimeUnit.Years); Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, rates, dc)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(new InterpolatedZeroCurve <Linear>(dates, divRates, dc)); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, 0.08, 1.5, 0.0625, 0.5, -0.8); HestonModel hestonModel = new HestonModel(hestonProcess); HullWhiteForwardProcess hwFwdProcess = new HullWhiteForwardProcess(rTS, 0.01, 0.01); hwFwdProcess.setForwardMeasureTime(dc.yearFraction(today, maturity)); HullWhite hullWhiteModel = new HullWhite(rTS, hwFwdProcess.a(), hwFwdProcess.sigma()); double tol = 0.002; double[] strike = { 80, 120 }; Option.Type[] types = { Option.Type.Put, Option.Type.Call }; for (int i = 0; i < types.Length; ++i) { for (int j = 0; j < strike.Length; ++j) { HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwFwdProcess, 0.0, HybridHestonHullWhiteProcess.Discretization.Euler); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], strike[j]); Exercise exercise = new EuropeanExercise(maturity); VanillaOption optionHestonHW = new VanillaOption(payoff, exercise); optionHestonHW.setPricingEngine(new MakeMCHestonHullWhiteEngine <PseudoRandom, Statistics>(jointProcess) .withSteps(1) .withAntitheticVariate() .withControlVariate() .withAbsoluteTolerance(tol) .withSeed(42).getAsPricingEngine()); VanillaOption optionPureHeston = new VanillaOption(payoff, exercise); optionPureHeston.setPricingEngine(new AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128)); double calculated = optionHestonHW.NPV(); double error = optionHestonHW.errorEstimate(); double expected = optionPureHeston.NPV(); if (Math.Abs(calculated - expected) > 3 * error && Math.Abs(calculated - expected) > tol) { QAssert.Fail("Failed to reproduce hw heston vanilla prices" + "\n strike: " + strike[j] + "\n calculated: " + calculated + "\n error: " + error + "\n expected: " + expected); } } } }