public void testLambdaBootstrapping() { //"Testing caplet LMM lambda bootstrapping..." //SavedSettings backup; double tolerance = 1e-10; double[] lambdaExpected = { 14.3010297550, 19.3821411939, 15.9816590141, 15.9953118303, 14.0570815635, 13.5687599894, 12.7477197786, 13.7056638165, 11.6191989567 }; LiborForwardModelProcess process = makeProcess(); Matrix covar = process.covariance(0.0, null, 1.0); for (int i = 0; i < 9; ++i) { double calculated = Math.Sqrt(covar[i + 1, i + 1]); double expected = lambdaExpected[i] / 100; if (Math.Abs(calculated - expected) > tolerance) { Assert.Fail("Failed to reproduce expected lambda values" + "\n calculated: " + calculated + "\n expected: " + expected); } } LfmCovarianceParameterization param = process.covarParam(); List <double> tmp = process.fixingTimes(); TimeGrid grid = new TimeGrid(tmp.Last(), 14); for (int t = 0; t < grid.size(); ++t) { //verifier la presence du null Matrix diff = param.integratedCovariance(grid[t], null) - param.integratedCovariance(grid[t], null); for (int i = 0; i < diff.rows(); ++i) { for (int j = 0; j < diff.columns(); ++j) { if (Math.Abs(diff[i, j]) > tolerance) { Assert.Fail("Failed to reproduce integrated covariance" + "\n calculated: " + diff[i, j] + "\n expected: " + 0); } } } } }
public void testInitialisation() { //"Testing caplet LMM process initialisation..." //SavedSettings backup; DayCounter dayCounter = new Actual360(); RelinkableHandle <YieldTermStructure> termStructure = new RelinkableHandle <YieldTermStructure>();; termStructure.linkTo(Utilities.flatRate(Date.Today, 0.04, dayCounter)); IborIndex index = new Euribor6M(termStructure); OptionletVolatilityStructure capletVol = new ConstantOptionletVolatility( termStructure.currentLink().referenceDate(), termStructure.currentLink().calendar(), BusinessDayConvention.Following, 0.2, termStructure.currentLink().dayCounter()); Calendar calendar = index.fixingCalendar(); for (int daysOffset = 0; daysOffset < 1825 /* 5 year*/; daysOffset += 8) { Date todaysDate = calendar.adjust(Date.Today + daysOffset); Settings.setEvaluationDate(todaysDate); Date settlementDate = calendar.advance(todaysDate, index.fixingDays(), TimeUnit.Days); termStructure.linkTo(Utilities.flatRate(settlementDate, 0.04, dayCounter)); LiborForwardModelProcess process = new LiborForwardModelProcess(60, index); List <double> fixings = process.fixingTimes(); for (int i = 1; i < fixings.Count - 1; ++i) { int ileft = process.nextIndexReset(fixings[i] - 0.000001); int iright = process.nextIndexReset(fixings[i] + 0.000001); int ii = process.nextIndexReset(fixings[i]); if ((ileft != i) || (iright != i + 1) || (ii != i + 1)) { Assert.Fail("Failed to next index resets"); } } } }
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 testCalibration() { // Testing calibration of a Libor forward model const int size = 14; const double tolerance = 8e-3; double[] capVols = { 0.145708, 0.158465, 0.166248, 0.168672, 0.169007, 0.167956, 0.166261, 0.164239, 0.162082, 0.159923, 0.157781, 0.155745, 0.153776, 0.151950, 0.150189, 0.148582, 0.147034, 0.145598, 0.144248 }; double[] swaptionVols = { 0.170595, 0.166844, 0.158306, 0.147444, 0.136930, 0.126833, 0.118135, 0.175963, 0.166359, 0.155203, 0.143712, 0.132769, 0.122947, 0.114310, 0.174455, 0.162265, 0.150539, 0.138734, 0.128215, 0.118470, 0.110540, 0.169780, 0.156860, 0.144821, 0.133537, 0.123167, 0.114363, 0.106500, 0.164521, 0.151223, 0.139670, 0.128632, 0.119123, 0.110330, 0.103114, 0.158956, 0.146036, 0.134555, 0.124393, 0.115038, 0.106996, 0.100064 }; IborIndex index = makeIndex(); LiborForwardModelProcess process = new LiborForwardModelProcess(size, index); Handle <YieldTermStructure> termStructure = index.forwardingTermStructure(); // set-up the model LmVolatilityModel volaModel = new LmExtLinearExponentialVolModel(process.fixingTimes(), 0.5, 0.6, 0.1, 0.1); LmCorrelationModel corrModel = new LmLinearExponentialCorrelationModel(size, 0.5, 0.8); LiborForwardModel model = new LiborForwardModel(process, volaModel, corrModel); int swapVolIndex = 0; DayCounter dayCounter = index.forwardingTermStructure().link.dayCounter(); // set-up calibration helper List <CalibrationHelper> calibrationHelper = new List <CalibrationHelper>(); int i; for (i = 2; i < size; ++i) { Period maturity = i * index.tenor(); Handle <Quote> capVol = new Handle <Quote>(new SimpleQuote(capVols[i - 2])); CalibrationHelper caphelper = new CapHelper(maturity, capVol, index, Frequency.Annual, index.dayCounter(), true, termStructure, CalibrationHelper.CalibrationErrorType.ImpliedVolError); caphelper.setPricingEngine(new AnalyticCapFloorEngine(model, termStructure)); calibrationHelper.Add(caphelper); if (i <= size / 2) { // add a few swaptions to test swaption calibration as well for (int j = 1; j <= size / 2; ++j) { Period len = j * index.tenor(); Handle <Quote> swaptionVol = new Handle <Quote>( new SimpleQuote(swaptionVols[swapVolIndex++])); CalibrationHelper swaptionHelper = new SwaptionHelper(maturity, len, swaptionVol, index, index.tenor(), dayCounter, index.dayCounter(), termStructure, CalibrationHelper.CalibrationErrorType.ImpliedVolError); swaptionHelper.setPricingEngine(new LfmSwaptionEngine(model, termStructure)); calibrationHelper.Add(swaptionHelper); } } } LevenbergMarquardt om = new LevenbergMarquardt(1e-6, 1e-6, 1e-6); //ConjugateGradient gc = new ConjugateGradient(); model.calibrate(calibrationHelper, om, new EndCriteria(2000, 100, 1e-6, 1e-6, 1e-6), new Constraint(), new List <double>()); // measure the calibration error double calculated = 0.0; for (i = 0; i < calibrationHelper.Count; ++i) { double diff = calibrationHelper[i].calibrationError(); calculated += diff * diff; } if (Math.Sqrt(calculated) > tolerance) { QAssert.Fail("Failed to calibrate libor forward model" + "\n calculated diff: " + Math.Sqrt(calculated) + "\n expected : smaller than " + tolerance); } }
public void testCapletPricing() { // Testing caplet pricing const int size = 10; #if QL_USE_INDEXED_COUPON const double tolerance = 1e-5; #else const double tolerance = 1e-12; #endif IborIndex index = makeIndex(); LiborForwardModelProcess process = new LiborForwardModelProcess(size, index); // set-up pricing engine OptionletVolatilityStructure capVolCurve = makeCapVolCurve(Settings.evaluationDate()); Vector variances = new LfmHullWhiteParameterization(process, capVolCurve).covariance(0.0, null).diagonal(); LmVolatilityModel volaModel = new LmFixedVolatilityModel(Vector.Sqrt(variances), process.fixingTimes()); LmCorrelationModel corrModel = new LmExponentialCorrelationModel(size, 0.3); IAffineModel model = (IAffineModel)(new LiborForwardModel(process, volaModel, corrModel)); Handle <YieldTermStructure> termStructure = process.index().forwardingTermStructure(); AnalyticCapFloorEngine engine1 = new AnalyticCapFloorEngine(model, termStructure); Cap cap1 = new Cap(process.cashFlows(), new InitializedList <double>(size, 0.04)); cap1.setPricingEngine(engine1); const double expected = 0.015853935178; double calculated = cap1.NPV(); if (Math.Abs(expected - calculated) > tolerance) { QAssert.Fail("Failed to reproduce npv" + "\n calculated: " + calculated + "\n expected: " + expected); } }
public void testMonteCarloCapletPricing() { //"Testing caplet LMM Monte-Carlo caplet pricing..." //SavedSettings backup; /* factor loadings are taken from Hull & White article * plus extra normalisation to get orthogonal eigenvectors * http://www.rotman.utoronto.ca/~amackay/fin/libormktmodel2.pdf */ double[] compValues = { 0.85549771, 0.46707264, 0.22353259, 0.91915359, 0.37716089, 0.11360610, 0.96438280, 0.26413316, -0.01412414, 0.97939148, 0.13492952, -0.15028753, 0.95970595, -0.00000000, -0.28100621, 0.97939148, -0.13492952, -0.15028753, 0.96438280, -0.26413316, -0.01412414, 0.91915359, -0.37716089, 0.11360610, 0.85549771, -0.46707264, 0.22353259 }; Matrix volaComp = new Matrix(9, 3); List <double> lcompValues = new InitializedList <double>(27, 0); List <double> ltemp = new InitializedList <double>(3, 0); lcompValues = compValues.ToList(); //std::copy(compValues, compValues+9*3, volaComp.begin()); for (int i = 0; i < 9; i++) { ltemp = lcompValues.GetRange(3 * i, 3); for (int j = 0; j < 3; j++) { volaComp[i, j] = ltemp[j]; } } LiborForwardModelProcess process1 = makeProcess(); LiborForwardModelProcess process2 = makeProcess(volaComp); List <double> tmp = process1.fixingTimes(); TimeGrid grid = new TimeGrid(tmp, 12); List <int> location = new List <int>(); for (int i = 0; i < tmp.Count; ++i) { location.Add(grid.index(tmp[i])); } // set-up a small Monte-Carlo simulation to price caplets // and ratchet caps using a one- and a three factor libor market model ulong seed = 42; LowDiscrepancy.icInstance = new InverseCumulativeNormal(); IRNG rsg1 = (IRNG) new LowDiscrepancy().make_sequence_generator( process1.factors() * (grid.size() - 1), seed); IRNG rsg2 = (IRNG) new LowDiscrepancy().make_sequence_generator( process2.factors() * (grid.size() - 1), seed); MultiPathGenerator <IRNG> generator1 = new MultiPathGenerator <IRNG> (process1, grid, rsg1, false); MultiPathGenerator <IRNG> generator2 = new MultiPathGenerator <IRNG> (process2, grid, rsg2, false); const int nrTrails = 250000; List <GeneralStatistics> stat1 = new InitializedList <GeneralStatistics>(process1.size()); List <GeneralStatistics> stat2 = new InitializedList <GeneralStatistics>(process2.size()); List <GeneralStatistics> stat3 = new InitializedList <GeneralStatistics>(process2.size() - 1); for (int i = 0; i < nrTrails; ++i) { Sample <MultiPath> path1 = generator1.next(); Sample <MultiPath> path2 = generator2.next(); List <double> rates1 = new InitializedList <double>(len); List <double> rates2 = new InitializedList <double>(len); for (int j = 0; j < process1.size(); ++j) { rates1[j] = path1.value[j][location[j]]; rates2[j] = path2.value[j][location[j]]; } List <double> dis1 = process1.discountBond(rates1); List <double> dis2 = process2.discountBond(rates2); for (int k = 0; k < process1.size(); ++k) { double accrualPeriod = process1.accrualEndTimes()[k] - process1.accrualStartTimes()[k]; // caplet payoff function, cap rate at 4% double payoff1 = Math.Max(rates1[k] - 0.04, 0.0) * accrualPeriod; double payoff2 = Math.Max(rates2[k] - 0.04, 0.0) * accrualPeriod; stat1[k].add(dis1[k] * payoff1); stat2[k].add(dis2[k] * payoff2); if (k != 0) { // ratchet cap payoff function double payoff3 = Math.Max(rates2[k] - (rates2[k - 1] + 0.0025), 0.0) * accrualPeriod; stat3[k - 1].add(dis2[k] * payoff3); } } } double[] capletNpv = { 0.000000000000, 0.000002841629, 0.002533279333, 0.009577143571, 0.017746502618, 0.025216116835, 0.031608230268, 0.036645683881, 0.039792254012, 0.041829864365 }; double[] ratchetNpv = { 0.0082644895, 0.0082754754, 0.0082159966, 0.0082982822, 0.0083803357, 0.0084366961, 0.0084173270, 0.0081803406, 0.0079533814 }; for (int k = 0; k < process1.size(); ++k) { double calculated1 = stat1[k].mean(); double tolerance1 = stat1[k].errorEstimate(); double expected = capletNpv[k]; if (Math.Abs(calculated1 - expected) > tolerance1) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated1 + "\n error int: " + tolerance1 + "\n expected: " + expected); } double calculated2 = stat2[k].mean(); double tolerance2 = stat2[k].errorEstimate(); if (Math.Abs(calculated2 - expected) > tolerance2) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated2 + "\n error int: " + tolerance2 + "\n expected: " + expected); } if (k != 0) { double calculated3 = stat3[k - 1].mean(); double tolerance3 = stat3[k - 1].errorEstimate(); expected = ratchetNpv[k - 1]; double refError = 1e-5; // 1e-5. error bars of the reference values if (Math.Abs(calculated3 - expected) > tolerance3 + refError) { Assert.Fail("Failed to reproduce expected caplet NPV" + "\n calculated: " + calculated3 + "\n error int: " + tolerance3 + refError + "\n expected: " + expected); } } } }