public void testFdmHestonEuropeanWithDividends() { //Testing FDM with European option with dividends in Heston model... using (SavedSettings backup = new SavedSettings()) { Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(100.0)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(0.05, new Actual365Fixed())); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(0.0, new Actual365Fixed())); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, 0.04, 2.5, 0.04, 0.66, -0.8); Settings.Instance.setEvaluationDate(new Date(28, 3, 2004)); Date exerciseDate = new Date(28, 3, 2005); Exercise exercise = new AmericanExercise(exerciseDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Put, 100); List <double> dividends = new InitializedList <double>(1, 5); List <Date> dividendDates = new InitializedList <Date>(1, new Date(28, 9, 2004)); DividendVanillaOption option = new DividendVanillaOption(payoff, exercise, dividendDates, dividends); IPricingEngine engine = new FdHestonVanillaEngine(new HestonModel(hestonProcess), 50, 100, 50); option.setPricingEngine(engine); double tol = 0.01; double gammaTol = 0.001; double npvExpected = 7.365075; double deltaExpected = -0.396678; double gammaExpected = 0.027681; if (Math.Abs(option.NPV() - npvExpected) > tol) { QAssert.Fail("Failed to reproduce expected npv" + "\n calculated: " + option.NPV() + "\n expected: " + npvExpected + "\n tolerance: " + tol); } if (Math.Abs(option.delta() - deltaExpected) > tol) { QAssert.Fail("Failed to reproduce expected delta" + "\n calculated: " + option.delta() + "\n expected: " + deltaExpected + "\n tolerance: " + tol); } if (Math.Abs(option.gamma() - gammaExpected) > gammaTol) { QAssert.Fail("Failed to reproduce expected gamma" + "\n calculated: " + option.gamma() + "\n expected: " + gammaExpected + "\n tolerance: " + tol); } } }
public void testFdmHestonIkonenToivanen() { //Testing FDM Heston for Ikonen and Toivanen tests... /* check prices of american puts as given in: * From Efficient numerical methods for pricing American options under * stochastic volatility, Samuli Ikonen, Jari Toivanen, * http://users.jyu.fi/~tene/papers/reportB12-05.pdf */ using (SavedSettings backup = new SavedSettings()) { Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(0.10, new Actual360())); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(0.0, new Actual360())); Settings.Instance.setEvaluationDate(new Date(28, 3, 2004)); Date exerciseDate = new Date(26, 6, 2004); Exercise exercise = new AmericanExercise(exerciseDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Put, 10); VanillaOption option = new VanillaOption(payoff, exercise); double[] strikes = new double[] { 8, 9, 10, 11, 12 }; double[] expected = new double[] { 2.00000, 1.10763, 0.520038, 0.213681, 0.082046 }; double tol = 0.001; for (int i = 0; i < strikes.Length; ++i) { Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(strikes[i])); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, 0.0625, 5, 0.16, 0.9, 0.1); IPricingEngine engine = new FdHestonVanillaEngine(new HestonModel(hestonProcess), 100, 400); option.setPricingEngine(engine); double calculated = option.NPV(); if (Math.Abs(calculated - expected[i]) > tol) { QAssert.Fail("Failed to reproduce expected npv" + "\n strike: " + strikes[i] + "\n calculated: " + calculated + "\n expected: " + expected[i] + "\n tolerance: " + tol); } } } }
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 ); } } } }
public void testMethodOfLinesAndCN() { //Testing method of lines to solve Heston PDEs... using (SavedSettings backup = new SavedSettings()) { DayCounter dc = new Actual365Fixed(); Date today = new Date(21, 2, 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.0, dc)); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, 0.0, dc)); double v0 = 0.09; double kappa = 1.0; double theta = v0; double sigma = 0.4; double rho = -0.75; Date maturity = today + new Period(3, TimeUnit.Months); HestonModel model = new HestonModel( new HestonProcess(rTS, qTS, spot, v0, kappa, theta, sigma, rho)); int xGrid = 21; int vGrid = 7; IPricingEngine fdmDefault = new FdHestonVanillaEngine(model, 10, xGrid, vGrid, 0); IPricingEngine fdmMol = new FdHestonVanillaEngine( model, 10, xGrid, vGrid, 0, new FdmSchemeDesc().MethodOfLines()); PlainVanillaPayoff payoff = new PlainVanillaPayoff(Option.Type.Put, spot.currentLink().value()); VanillaOption option = new VanillaOption(payoff, new AmericanExercise(maturity)); option.setPricingEngine(fdmMol); double calculatedMoL = option.NPV(); option.setPricingEngine(fdmDefault); double expected = option.NPV(); double tol = 0.005; double diffMoL = Math.Abs(expected - calculatedMoL); if (diffMoL > tol) { QAssert.Fail("Failed to reproduce european option values with MOL" + "\n calculated: " + calculatedMoL + "\n expected: " + expected + "\n difference: " + diffMoL + "\n tolerance: " + tol); } IPricingEngine fdmCN = new FdHestonVanillaEngine(model, 10, xGrid, vGrid, 0, new FdmSchemeDesc().CrankNicolson()); option.setPricingEngine(fdmCN); double calculatedCN = option.NPV(); double diffCN = Math.Abs(expected - calculatedCN); if (diffCN > tol) { QAssert.Fail("Failed to reproduce european option values with Crank-Nicolson" + "\n calculated: " + calculatedCN + "\n expected: " + expected + "\n difference: " + diffCN + "\n tolerance: " + tol); } BarrierOption barrierOption = new BarrierOption(Barrier.Type.DownOut, 85.0, 10.0, payoff, new EuropeanExercise(maturity)); barrierOption.setPricingEngine(new FdHestonBarrierEngine(model, 100, 31, 11)); double expectedBarrier = barrierOption.NPV(); barrierOption.setPricingEngine(new FdHestonBarrierEngine(model, 100, 31, 11, 0, new FdmSchemeDesc().MethodOfLines())); double calculatedBarrierMoL = barrierOption.NPV(); double barrierTol = 0.01; double barrierDiffMoL = Math.Abs(expectedBarrier - calculatedBarrierMoL); if (barrierDiffMoL > barrierTol) { QAssert.Fail("Failed to reproduce barrier option values with MOL" + "\n calculated: " + calculatedBarrierMoL + "\n expected: " + expectedBarrier + "\n difference: " + barrierDiffMoL + "\n tolerance: " + barrierTol); } barrierOption.setPricingEngine(new FdHestonBarrierEngine(model, 100, 31, 11, 0, new FdmSchemeDesc().CrankNicolson())); double calculatedBarrierCN = barrierOption.NPV(); double barrierDiffCN = Math.Abs(expectedBarrier - calculatedBarrierCN); if (barrierDiffCN > barrierTol) { QAssert.Fail("Failed to reproduce barrier option values with Crank-Nicolson" + "\n calculated: " + calculatedBarrierCN + "\n expected: " + expectedBarrier + "\n difference: " + barrierDiffCN + "\n tolerance: " + barrierTol); } } }
public void testFdmHestonConvergence() { /* convergence tests based on * ADI finite difference schemes for option pricing in the * Heston model with correlation, K.J. in t'Hout and S. Foulon */ //Testing FDM Heston convergence... using (SavedSettings backup = new SavedSettings()) { HestonTestData[] values = new HestonTestData[] { new HestonTestData(1.5, 0.04, 0.3, -0.9, 0.025, 0.0, 1.0, 100), new HestonTestData(3.0, 0.12, 0.04, 0.6, 0.01, 0.04, 1.0, 100), new HestonTestData(0.6067, 0.0707, 0.2928, -0.7571, 0.03, 0.0, 3.0, 100), new HestonTestData(2.5, 0.06, 0.5, -0.1, 0.0507, 0.0469, 0.25, 100) }; FdmSchemeDesc[] schemes = new FdmSchemeDesc[] { new FdmSchemeDesc().Hundsdorfer(), new FdmSchemeDesc().ModifiedCraigSneyd(), new FdmSchemeDesc().ModifiedHundsdorfer(), new FdmSchemeDesc().CraigSneyd(), new FdmSchemeDesc().TrBDF2(), new FdmSchemeDesc().CrankNicolson(), }; int[] tn = new int[] { 60 }; double[] v0 = new double[] { 0.04 }; Date todaysDate = new Date(28, 3, 2004); Settings.Instance.setEvaluationDate(todaysDate); Handle <Quote> s0 = new Handle <Quote>(new SimpleQuote(75.0)); for (int l = 0; l < schemes.Length; ++l) { for (int i = 0; i < values.Length; ++i) { for (int j = 0; j < tn.Length; ++j) { for (int k = 0; k < v0.Length; ++k) { Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>( Utilities.flatRate(values[i].r, new Actual365Fixed())); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>( Utilities.flatRate(values[i].q, new Actual365Fixed())); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, s0, v0[k], values[i].kappa, values[i].theta, values[i].sigma, values[i].rho); Date exerciseDate = todaysDate + new Period(Convert.ToInt32(values[i].T * 365), TimeUnit.Days); Exercise exercise = new EuropeanExercise(exerciseDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, values[i].K); VanillaOption option = new VanillaOption(payoff, exercise); IPricingEngine engine = new FdHestonVanillaEngine( new HestonModel(hestonProcess), tn[j], 101, 51, 0, schemes[l]); option.setPricingEngine(engine); double calculated = option.NPV(); IPricingEngine analyticEngine = new AnalyticHestonEngine( new HestonModel(hestonProcess), 144); option.setPricingEngine(analyticEngine); double expected = option.NPV(); if (Math.Abs(expected - calculated) / expected > 0.02 && Math.Abs(expected - calculated) > 0.002) { QAssert.Fail("Failed to reproduce expected npv" + "\n calculated: " + calculated + "\n expected: " + expected + "\n tolerance: " + 0.01); } } } } } } }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(FdHestonVanillaEngine obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }