internal static global::System.Runtime.InteropServices.HandleRef getCPtr(HullWhiteForwardProcess obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
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); } } } }
public void testCallableEquityPricing() { // Testing the pricing of a callable equity product /* * For the definition of the example product see * Alexander Giese, On the Pricing of Auto-Callable Equity * Structures in the Presence of Stochastic Volatility and * Stochastic Interest Rates . * http://workshop.mathfinance.de/2006/papers/giese/slides.pdf */ int maturity = 7; DayCounter dc = new Actual365Fixed(); Date today = Date.Today; 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.04); Handle <YieldTermStructure> rTS = new Handle <YieldTermStructure>(Utilities.flatRate(today, rRate, dc)); HestonProcess hestonProcess = new HestonProcess(rTS, qTS, spot, 0.0625, 1.0, 0.24 * 0.24, 1e-4, 0.0); // FLOATING_POINT_EXCEPTION HullWhiteForwardProcess hwProcess = new HullWhiteForwardProcess(rTS, 0.00883, 0.00526); hwProcess.setForwardMeasureTime(dc.yearFraction(today, today + new Period(maturity + 1, TimeUnit.Years))); HybridHestonHullWhiteProcess jointProcess = new HybridHestonHullWhiteProcess(hestonProcess, hwProcess, -0.4); Schedule schedule = new Schedule(today, today + new Period(maturity, TimeUnit.Years), new Period(1, TimeUnit.Years), new TARGET(), BusinessDayConvention.Following, BusinessDayConvention.Following, DateGeneration.Rule.Forward, false); List <double> times = new InitializedList <double>(maturity + 1); for (int i = 0; i <= maturity; ++i) { times[i] = i; } TimeGrid grid = new TimeGrid(times, times.Count); List <double> redemption = new InitializedList <double>(maturity); for (int i = 0; i < maturity; ++i) { redemption[i] = 1.07 + 0.03 * i; } ulong seed = 42; IRNG rsg = (InverseCumulativeRsg <RandomSequenceGenerator <MersenneTwisterUniformRng> , InverseCumulativeNormal>) new PseudoRandom().make_sequence_generator(jointProcess.factors() * (grid.size() - 1), seed); MultiPathGenerator <IRNG> generator = new MultiPathGenerator <IRNG>(jointProcess, grid, rsg, false); GeneralStatistics stat = new GeneralStatistics(); double antitheticPayoff = 0; int nrTrails = 40000; for (int i = 0; i < nrTrails; ++i) { bool antithetic = (i % 2) != 0; Sample <IPath> path = antithetic ? generator.antithetic() : generator.next(); MultiPath value = path.value as MultiPath; Utils.QL_REQUIRE(value != null, () => "Invalid Path"); double payoff = 0; for (int j = 1; j <= maturity; ++j) { if (value[0][j] > spot.link.value()) { Vector states = new Vector(3); for (int k = 0; k < 3; ++k) { states[k] = value[k][j]; } payoff = redemption[j - 1] / jointProcess.numeraire(grid[j], states); break; } else if (j == maturity) { Vector states = new Vector(3); for (int k = 0; k < 3; ++k) { states[k] = value[k][j]; } payoff = 1.0 / jointProcess.numeraire(grid[j], states); } } if (antithetic) { stat.add(0.5 * (antitheticPayoff + payoff)); } else { antitheticPayoff = payoff; } } double expected = 0.938; double calculated = stat.mean(); double error = stat.errorEstimate(); if (Math.Abs(expected - calculated) > 3 * error) { QAssert.Fail("Failed to reproduce auto-callable equity structure price" + "\n calculated: " + calculated + "\n error: " + error + "\n expected: " + expected); } }
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); } } }