public void testSwaptionPricing() { // Testing forward swap and swaption pricing const int size = 10; const int steps = 8 * size; #if QL_USE_INDEXED_COUPON const double tolerance = 1e-6; #else const double tolerance = 1e-12; #endif List <Date> dates = new List <Date>(); List <double> rates = new List <double>(); dates.Add(new Date(4, 9, 2005)); dates.Add(new Date(4, 9, 2011)); rates.Add(0.04); rates.Add(0.08); IborIndex index = makeIndex(dates, rates); LiborForwardModelProcess process = new LiborForwardModelProcess(size, index); LmCorrelationModel corrModel = new LmExponentialCorrelationModel(size, 0.5); LmVolatilityModel volaModel = new LmLinearExponentialVolatilityModel(process.fixingTimes(), 0.291, 1.483, 0.116, 0.00001); // set-up pricing engine process.setCovarParam((LfmCovarianceParameterization) new LfmCovarianceProxy(volaModel, corrModel)); // set-up a small Monte-Carlo simulation to price swations List <double> tmp = process.fixingTimes(); TimeGrid grid = new TimeGrid(tmp, tmp.Count, steps); List <int> location = new List <int>(); for (int i = 0; i < tmp.Count; ++i) { location.Add(grid.index(tmp[i])); } ulong seed = 42; const int nrTrails = 5000; LowDiscrepancy.icInstance = new InverseCumulativeNormal(); IRNG rsg = (InverseCumulativeRsg <RandomSequenceGenerator <MersenneTwisterUniformRng> , InverseCumulativeNormal>) new PseudoRandom().make_sequence_generator(process.factors() * (grid.size() - 1), seed); MultiPathGenerator <IRNG> generator = new MultiPathGenerator <IRNG>(process, grid, rsg, false); LiborForwardModel liborModel = new LiborForwardModel(process, volaModel, corrModel); Calendar calendar = index.fixingCalendar(); DayCounter dayCounter = index.forwardingTermStructure().link.dayCounter(); BusinessDayConvention convention = index.businessDayConvention(); Date settlement = index.forwardingTermStructure().link.referenceDate(); SwaptionVolatilityMatrix m = liborModel.getSwaptionVolatilityMatrix(); for (int i = 1; i < size; ++i) { for (int j = 1; j <= size - i; ++j) { Date fwdStart = settlement + new Period(6 * i, TimeUnit.Months); Date fwdMaturity = fwdStart + new Period(6 * j, TimeUnit.Months); Schedule schedule = new Schedule(fwdStart, fwdMaturity, index.tenor(), calendar, convention, convention, DateGeneration.Rule.Forward, false); double swapRate = 0.0404; VanillaSwap forwardSwap = new VanillaSwap(VanillaSwap.Type.Receiver, 1.0, schedule, swapRate, dayCounter, schedule, index, 0.0, index.dayCounter()); forwardSwap.setPricingEngine(new DiscountingSwapEngine(index.forwardingTermStructure())); // check forward pricing first double expected = forwardSwap.fairRate(); double calculated = liborModel.S_0(i - 1, i + j - 1); if (Math.Abs(expected - calculated) > tolerance) { QAssert.Fail("Failed to reproduce fair forward swap rate" + "\n calculated: " + calculated + "\n expected: " + expected); } swapRate = forwardSwap.fairRate(); forwardSwap = new VanillaSwap(VanillaSwap.Type.Receiver, 1.0, schedule, swapRate, dayCounter, schedule, index, 0.0, index.dayCounter()); forwardSwap.setPricingEngine(new DiscountingSwapEngine(index.forwardingTermStructure())); if (i == j && i <= size / 2) { IPricingEngine engine = new LfmSwaptionEngine(liborModel, index.forwardingTermStructure()); Exercise exercise = new EuropeanExercise(process.fixingDates()[i]); Swaption swaption = new Swaption(forwardSwap, exercise); swaption.setPricingEngine(engine); GeneralStatistics stat = new GeneralStatistics(); for (int n = 0; n < nrTrails; ++n) { Sample <IPath> path = (n % 2 != 0) ? generator.antithetic() : generator.next(); MultiPath value = path.value as MultiPath; Utils.QL_REQUIRE(value != null, () => "Invalid Path"); //Sample<MultiPath> path = generator.next(); List <double> rates_ = new InitializedList <double>(size); for (int k = 0; k < process.size(); ++k) { rates_[k] = value[k][location[i]]; } List <double> dis = process.discountBond(rates_); double npv = 0.0; for (int k = i; k < i + j; ++k) { npv += (swapRate - rates_[k]) * (process.accrualEndTimes()[k] - process.accrualStartTimes()[k]) * dis[k]; } stat.add(Math.Max(npv, 0.0)); } if (Math.Abs(swaption.NPV() - stat.mean()) > stat.errorEstimate() * 2.35) { QAssert.Fail("Failed to reproduce swaption npv" + "\n calculated: " + stat.mean() + "\n expected: " + swaption.NPV()); } } } } }
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); } }