public void testBsmHullWhiteEngine() { // Testing European option pricing for a BSM process with one-factor Hull-White model DayCounter dc = new Actual365Fixed(); Date today = Date.Today; Date maturity = today + new Period(20, TimeUnit.Years); Settings.Instance.setEvaluationDate(today); Handle <Quote> spot = new Handle <Quote>(new SimpleQuote(100.0)); SimpleQuote qRate = new SimpleQuote(0.04); Handle <YieldTermStructure> qTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, qRate, dc)); SimpleQuote rRate = new SimpleQuote(0.0525); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, rRate, dc)); SimpleQuote vol = new SimpleQuote(0.25); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); // FLOATING_POINT_EXCEPTION HullWhite hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), 0.00883, 0.00526); BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(spot, qTS, rTS, volTS); Exercise exercise = new EuropeanExercise(maturity); double fwd = spot.link.value() * qTS.link.discount(maturity) / rTS.link.discount(maturity); StrikedTypePayoff payoff = new PlainVanillaPayoff(Option.Type.Call, fwd); EuropeanOption option = new EuropeanOption(payoff, exercise); double tol = 1e-8; double[] corr = { -0.75, -0.25, 0.0, 0.25, 0.75 }; double[] expectedVol = { 0.217064577, 0.243995801, 0.256402830, 0.268236596, 0.290461343 }; for (int i = 0; i < corr.Length; ++i) { IPricingEngine bsmhwEngine = new AnalyticBSMHullWhiteEngine(corr[i], stochProcess, hullWhiteModel); option.setPricingEngine(bsmhwEngine); double npv = option.NPV(); Handle <BlackVolTermStructure> compVolTS = new Handle <BlackVolTermStructure>( Utilities.flatVol(today, expectedVol[i], dc)); BlackScholesMertonProcess bsProcess = new BlackScholesMertonProcess(spot, qTS, rTS, compVolTS); IPricingEngine bsEngine = new AnalyticEuropeanEngine(bsProcess); EuropeanOption comp = new EuropeanOption(payoff, exercise); comp.setPricingEngine(bsEngine); double impliedVol = comp.impliedVolatility(npv, bsProcess, 1e-10, 100); if (Math.Abs(impliedVol - expectedVol[i]) > tol) { QAssert.Fail("Failed to reproduce implied volatility" + "\n calculated: " + impliedVol + "\n expected : " + expectedVol[i]); } if (Math.Abs((comp.NPV() - npv) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs(comp.delta() - option.delta()) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.gamma() - option.gamma()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.theta() - option.theta()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } if (Math.Abs((comp.vega() - option.vega()) / npv) > tol) { QAssert.Fail("Failed to reproduce NPV" + "\n calculated: " + npv + "\n expected : " + comp.NPV()); } } }
public void testCompareBsmHWandHestonHW() { // Comparing European option pricing for a BSM process with one-factor Hull-White model DayCounter dc = new Actual365Fixed(); Date today = Date.Today; Settings.Instance.setEvaluationDate(today); Handle <Quote> spot = new Handle <Quote>(new SimpleQuote(100.0)); List <Date> dates = new List <Date>(); 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.01 + 0.0002 * Math.Exp(Math.Sin(i / 4.0))); divRates.Add(0.02 + 0.0001 * Math.Exp(Math.Sin(i / 5.0))); } 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)); SimpleQuote vol = new SimpleQuote(0.25); Handle <BlackVolTermStructure> volTS = new Handle <BlackVolTermStructure>(Utilities.flatVol(today, vol, dc)); BlackScholesMertonProcess bsmProcess = new BlackScholesMertonProcess(spot, qTS, rTS, volTS); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, spot, vol.value() * vol.value(), 1.0, vol.value() * vol.value(), 1e-4, 0.0); HestonModel hestonModel = new HestonModel(hestonProcess); HullWhite hullWhiteModel = new HullWhite(new Handle <YieldTermStructure>(rTS), 0.01, 0.01); IPricingEngine bsmhwEngine = new AnalyticBSMHullWhiteEngine(0.0, bsmProcess, hullWhiteModel); IPricingEngine hestonHwEngine = new AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128); double tol = 1e-5; double[] strike = { 0.25, 0.5, 0.75, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 4.0 }; int[] maturity = { 1, 2, 3, 5, 10, 15, 20, 25, 30 }; 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) { for (int l = 0; l < maturity.Length; ++l) { Date maturityDate = today + new Period(maturity[l], TimeUnit.Years); Exercise exercise = new EuropeanExercise(maturityDate); double fwd = strike[j] * spot.link.value() * qTS.link.discount(maturityDate) / rTS.link.discount(maturityDate); StrikedTypePayoff payoff = new PlainVanillaPayoff(types[i], fwd); EuropeanOption option = new EuropeanOption(payoff, exercise); option.setPricingEngine(bsmhwEngine); double calculated = option.NPV(); option.setPricingEngine(hestonHwEngine); double expected = option.NPV(); if (Math.Abs(calculated - expected) > calculated * tol && Math.Abs(calculated - expected) > tol) { QAssert.Fail("Failed to reproduce npvs" + "\n calculated: " + calculated + "\n expected : " + expected + "\n strike : " + strike[j] + "\n maturity : " + maturity[l] + "\n type : " + ((types[i] == QLCore.Option.Type.Put) ? "Put" : "Call")); } } } } }