public override void calculate() { Utils.QL_REQUIRE(arguments_.settlementMethod != Settlement.Method.ParYieldCurve, () => "cash-settled (ParYieldCurve) swaptions not priced by Jamshidian engine"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "cannot use the Jamshidian decomposition on exotic swaptions"); Utils.QL_REQUIRE(arguments_.swap.spread.IsEqual(0.0), () => "non zero spread (" + arguments_.swap.spread + ") not allowed"); Date referenceDate; DayCounter dayCounter; ITermStructureConsistentModel tsmodel = (ITermStructureConsistentModel)base.model_.link; try { if (tsmodel != null) { referenceDate = tsmodel.termStructure().link.referenceDate(); dayCounter = tsmodel.termStructure().link.dayCounter(); } else { referenceDate = termStructure_.link.referenceDate(); dayCounter = termStructure_.link.dayCounter(); } } catch { referenceDate = termStructure_.link.referenceDate(); dayCounter = termStructure_.link.dayCounter(); } List <double> amounts = new InitializedList <double>(arguments_.fixedCoupons.Count); for (int i = 0; i < amounts.Count; i++) { amounts[i] = arguments_.fixedCoupons[i]; } amounts[amounts.Count - 1] = amounts.Last() + arguments_.nominal; double maturity = dayCounter.yearFraction(referenceDate, arguments_.exercise.date(0)); List <double> fixedPayTimes = new InitializedList <double>(arguments_.fixedPayDates.Count); for (int i = 0; i < fixedPayTimes.Count; i++) { fixedPayTimes[i] = dayCounter.yearFraction(referenceDate, arguments_.fixedPayDates[i]); } rStarFinder finder = new rStarFinder(model_, arguments_.nominal, maturity, fixedPayTimes, amounts); Brent s1d = new Brent(); double minStrike = -10.0; double maxStrike = 10.0; s1d.setMaxEvaluations(10000); s1d.setLowerBound(minStrike); s1d.setUpperBound(maxStrike); double rStar = s1d.solve(finder, 1e-8, 0.05, minStrike, maxStrike); Option.Type w = arguments_.type == VanillaSwap.Type.Payer ? Option.Type.Put : Option.Type.Call; int size = arguments_.fixedCoupons.Count; double value = 0.0; for (int i = 0; i < size; i++) { double fixedPayTime = dayCounter.yearFraction(referenceDate, arguments_.fixedPayDates[i]); double strike = model_.link.discountBond(maturity, fixedPayTime, rStar); double dboValue = model_.link.discountBondOption( w, strike, maturity, fixedPayTime); value += amounts[i] * dboValue; } results_.value = value; }
public FdmHestonVarianceMesher(int size, HestonProcess process, double maturity, int tAvgSteps = 10, double epsilon = 0.0001) : base(size) { List <double> vGrid = new InitializedList <double>(size, 0.0); List <double> pGrid = new InitializedList <double>(size, 0.0); double df = 4.0 * process.theta() * process.kappa() / Math.Pow(process.sigma(), 2); try { List <pair_double> grid = new List <pair_double>(); for (int l = 1; l <= tAvgSteps; ++l) { double t = (maturity * l) / tAvgSteps; double ncp = 4 * process.kappa() * Math.Exp(-process.kappa() * t) / (Math.Pow(process.sigma(), 2) * (1 - Math.Exp(-process.kappa() * t))) * process.v0(); double k = Math.Pow(process.sigma(), 2) * (1 - Math.Exp(-process.kappa() * t)) / (4 * process.kappa()); double qMin = 0.0; // v_min = 0.0; double qMax = Math.Max(process.v0(), k * new InverseNonCentralCumulativeChiSquareDistribution( df, ncp, 100, 1e-8).value(1 - epsilon)); double minVStep = (qMax - qMin) / (50 * size); double ps, p = 0.0; double vTmp = qMin; grid.Add(new pair_double(qMin, epsilon)); for (int i = 1; i < size; ++i) { ps = (1 - epsilon - p) / (size - i); p += ps; double tmp = k * new InverseNonCentralCumulativeChiSquareDistribution( df, ncp, 100, 1e-8).value(p); double vx = Math.Max(vTmp + minVStep, tmp); p = new NonCentralCumulativeChiSquareDistribution(df, ncp).value(vx / k); vTmp = vx; grid.Add(new pair_double(vx, p)); } } Utils.QL_REQUIRE(grid.Count == size * tAvgSteps, () => "something wrong with the grid size"); grid.Sort(); List <Pair <double, double> > tp = new List <Pair <double, double> >(grid); for (int i = 0; i < size; ++i) { int b = (i * tp.Count) / size; int e = ((i + 1) * tp.Count) / size; for (int j = b; j < e; ++j) { vGrid[i] += tp[j].first / (e - b); pGrid[i] += tp[j].second / (e - b); } } } catch (Exception) { // use default mesh double vol = process.sigma() * Math.Sqrt(process.theta() / (2 * process.kappa())); double mean = process.theta(); double upperBound = Math.Max(process.v0() + 4 * vol, mean + 4 * vol); double lowerBound = Math.Max(0.0, Math.Min(process.v0() - 4 * vol, mean - 4 * vol)); for (int i = 0; i < size; ++i) { pGrid[i] = i / (size - 1.0); vGrid[i] = lowerBound + i * (upperBound - lowerBound) / (size - 1.0); } } double skewHint = ((process.kappa() != 0.0) ? Math.Max(1.0, process.sigma() / process.kappa()) : 1.0); pGrid.Sort(); volaEstimate_ = new GaussLobattoIntegral(100000, 1e-4).value( new interpolated_volatility(pGrid, vGrid).value, pGrid.First(), pGrid.Last()) * Math.Pow(skewHint, 1.5); double v0 = process.v0(); for (int i = 1; i < vGrid.Count; ++i) { if (vGrid[i - 1] <= v0 && vGrid[i] >= v0) { if (Math.Abs(vGrid[i - 1] - v0) < Math.Abs(vGrid[i] - v0)) { vGrid[i - 1] = v0; } else { vGrid[i] = v0; } } } locations_ = vGrid.Select(x => x).ToList(); for (int i = 0; i < size - 1; ++i) { dminus_[i + 1] = dplus_[i] = vGrid[i + 1] - vGrid[i]; } dplus_[dplus_.Count - 1] = null; dminus_[0] = null; }