public void testSpuriousOscillations() { //Testing for spurious oscillations when solving the Heston PDEs... using (SavedSettings backup = new SavedSettings()) { DayCounter dc = new Actual365Fixed(); Date today = new Date(7, 6, 2018); Settings.Instance.setEvaluationDate(today); Handle <Quote> spot = new Handle <Quote>(new SimpleQuote(100.0)); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, 0.1, dc)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, 0.0, dc)); double v0 = 0.005; double kappa = 1.0; double theta = 0.005; double sigma = 0.4; double rho = -0.75; Date maturity = today + new Period(1, TimeUnit.Years); HestonProcess process = new HestonProcess( rTS, qTS, spot, v0, kappa, theta, sigma, rho); HestonModel model = new HestonModel(process); FdHestonVanillaEngine hestonEngine = new FdHestonVanillaEngine( model, 6, 200, 13, 0, new FdmSchemeDesc().TrBDF2()); VanillaOption option = new VanillaOption(new PlainVanillaPayoff(Option.Type.Call, spot.currentLink().value()), new EuropeanExercise(maturity)); option.setupArguments(hestonEngine.getArguments()); List <Tuple <FdmSchemeDesc, string, bool> > descs = new List <Tuple <FdmSchemeDesc, string, bool> >(); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().CraigSneyd(), "Craig-Sneyd", true)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().Hundsdorfer(), "Hundsdorfer", true)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().ModifiedHundsdorfer(), "Mod. Hundsdorfer", true)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().Douglas(), "Douglas", true)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().CrankNicolson(), "Crank-Nicolson", true)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().ImplicitEuler(), "Implicit", false)); descs.Add(new Tuple <FdmSchemeDesc, string, bool>(new FdmSchemeDesc().TrBDF2(), "TR-BDF2", false)); for (int j = 0; j < descs.Count; ++j) { FdmHestonSolver solver = new FdmHestonSolver(new Handle <HestonProcess>(process), hestonEngine.getSolverDesc(1.0), descs[j].Item1); List <double> gammas = new List <double>(); for (double x = 99; x < 101.001; x += 0.1) { gammas.Add(solver.gammaAt(x, v0)); } double maximum = Double.MinValue; for (int i = 1; i < gammas.Count; ++i) { double diff = Math.Abs(gammas[i] - gammas[i - 1]); if (diff > maximum) { maximum = diff; } } double tol = 0.01; bool hasSpuriousOscillations = maximum > tol; if (hasSpuriousOscillations != descs[j].Item3) { QAssert.Fail("unable to reproduce spurious oscillation behaviour " + "\n scheme name : " + descs[j].Item2 + "\n oscillations observed: " + hasSpuriousOscillations + "\n oscillations expected: " + descs[j].Item3 ); } } } }