// This is the continuous version of a characteristic function // for the exact sampling of the Heston process, s. page 8, formula 13, // M. Broadie, O. Kaya, Exact Simulation of Stochastic Volatility and // other Affine Jump Diffusion Processes // http://finmath.stanford.edu/seminars/documents/Broadie.pdf // // This version does not need a branch correction procedure. // For details please see: // Roger Lord, "Efficient Pricing Algorithms for exotic Derivatives", // http://repub.eur.nl/pub/13917/LordR-Thesis.pdf Complex Phi(HestonProcess process, Complex a, double nu_0, double nu_t, double dt) { double theta = process.theta(); double kappa = process.kappa(); double sigma = process.sigma(); double sigma2 = sigma * sigma; Complex ga = Complex.Sqrt(kappa * kappa - 2 * sigma2 * a * new Complex(0.0, 1.0)); double d = 4 * theta * kappa / sigma2; double nu = 0.5 * d - 1; Complex z = ga * Complex.Exp(-0.5 * ga * dt) / (1.0 - Complex.Exp(-ga * dt)); Complex log_z = -0.5 * ga * dt + Complex.Log(ga / (1.0 - Complex.Exp(-ga * dt))); Complex alpha = 4.0 * ga * Complex.Exp(-0.5 * ga * dt) / (sigma2 * (1.0 - Complex.Exp(-ga * dt))); Complex beta = 4.0 * kappa * Complex.Exp(-0.5 * kappa * dt) / (sigma2 * (1.0 - Complex.Exp(-kappa * dt))); return(ga * Complex.Exp(-0.5 * (ga - kappa) * dt) * (1 - Complex.Exp(-kappa * dt)) / (kappa * (1.0 - Complex.Exp(-ga * dt))) * Complex.Exp((nu_0 + nu_t) / sigma2 * ( kappa * (1.0 + Complex.Exp(-kappa * dt)) / (1.0 - Complex.Exp(-kappa * dt)) - ga * (1.0 + Complex.Exp(-ga * dt)) / (1.0 - Complex.Exp(-ga * dt)))) * Complex.Exp(nu * log_z) / Complex.Pow(z, nu) * ((nu_t > 1e-8) ? Utils.modifiedBesselFunction_i(nu, Complex.Sqrt(nu_0 * nu_t) * alpha) / Utils.modifiedBesselFunction_i(nu, Complex.Sqrt(nu_0 * nu_t) * beta) : Complex.Pow(alpha / beta, nu) )); }
private double cornishFisherEps(HestonProcess process, double nu_0, double nu_t, double dt, double eps) { // use moment generating function to get the // first,second, third and fourth moment of the distribution double d = 1e-2; double p2 = Phi(process, new Complex(0, -2 * d), nu_0, nu_t, dt).Real; double p1 = Phi(process, new Complex(0, -d), nu_0, nu_t, dt).Real; double p0 = Phi(process, new Complex(0, 0), nu_0, nu_t, dt).Real; double pm1 = Phi(process, new Complex(0, d), nu_0, nu_t, dt).Real; double pm2 = Phi(process, new Complex(0, 2 * d), nu_0, nu_t, dt).Real; double avg = (pm2 - 8 * pm1 + 8 * p1 - p2) / (12 * d); double m2 = (-pm2 + 16 * pm1 - 30 * p0 + 16 * p1 - p2) / (12 * d * d); double var = m2 - avg * avg; double stdDev = Math.Sqrt(var); double m3 = (-0.5 * pm2 + pm1 - p1 + 0.5 * p2) / (d * d * d); double skew = (m3 - 3 * var * avg - avg * avg * avg) / (var * stdDev); double m4 = (pm2 - 4 * pm1 + 6 * p0 - 4 * p1 + p2) / (d * d * d * d); double kurt = (m4 - 4 * m3 * avg + 6 * m2 * avg * avg - 3 * avg * avg * avg * avg) / (var * var); // Cornish-Fisher relation to come up with an improved // estimate of 1-F(u_\eps) < \eps double q = new InverseCumulativeNormal().value(1 - eps); double w = q + (q * q - 1) / 6 * skew + (q * q * q - 3 * q) / 24 * (kurt - 3) - (2 * q * q * q - 5 * q) / 36 * skew * skew; return(avg + w * stdDev); }
public HybridHestonHullWhiteProcess(HestonProcess hestonProcess, HullWhiteForwardProcess hullWhiteProcess, double corrEquityShortRate, Discretization discretization = Discretization.BSMHullWhite) { hestonProcess_ = hestonProcess; hullWhiteProcess_ = hullWhiteProcess; hullWhiteModel_ = new HullWhite(hestonProcess.riskFreeRate(), hullWhiteProcess.a(), hullWhiteProcess.sigma()); corrEquityShortRate_ = corrEquityShortRate; disc_ = discretization; maxRho_ = Math.Sqrt(1 - hestonProcess.rho() * hestonProcess.rho()) - Math.Sqrt(Const.QL_EPSILON) /* reserve for rounding errors */; T_ = hullWhiteProcess.getForwardMeasureTime(); endDiscount_ = hestonProcess.riskFreeRate().link.discount(T_); Utils.QL_REQUIRE(corrEquityShortRate * corrEquityShortRate + hestonProcess.rho() * hestonProcess.rho() <= 1.0, () => "correlation matrix is not positive definite"); Utils.QL_REQUIRE(hullWhiteProcess.sigma() > 0.0, () => "positive vol of Hull White process is required"); }
protected override void generateArguments() { process_ = new HestonProcess(process_.riskFreeRate(), process_.dividendYield(), process_.s0(), v0(), kappa(), theta(), sigma(), rho()); }
public ch(HestonProcess _process, double _x, double _nu_0, double _nu_t, double _dt) { process = _process; x = _x; nu_0 = _nu_0; nu_t = _nu_t; dt = _dt; }
public FdmHestonLocalVolatilityVarianceMesher(int size, HestonProcess process, LocalVolTermStructure leverageFct, double maturity, int tAvgSteps = 10, double epsilon = 0.0001) : base(size) { leverageFct_ = leverageFct; FdmHestonVarianceMesher mesher = new FdmHestonVarianceMesher(size, process, maturity, tAvgSteps, epsilon); for (int i = 0; i < size; ++i) { dplus_[i] = mesher.dplus(i); dminus_[i] = mesher.dminus(i); locations_[i] = mesher.location(i); } volaEstimate_ = mesher.volaEstimate(); if (leverageFct != null) { double s0 = process.s0().currentLink().value(); List <double> acc = new List <double>(); acc.Add(leverageFct.localVol(0.0, s0, true)); Handle <YieldTermStructure> rTS = process.riskFreeRate(); Handle <YieldTermStructure> qTS = process.dividendYield(); for (int l = 1; l <= tAvgSteps; ++l) { double t = (maturity * l) / tAvgSteps; double vol = volaEstimate_ * acc.Average(); double fwd = s0 * qTS.currentLink().discount(t) / rTS.currentLink().discount(t); int sAvgSteps = 50; Vector u = new Vector(sAvgSteps), sig = new Vector(sAvgSteps); for (int i = 0; i < sAvgSteps; ++i) { u[i] = epsilon + ((1.0 - 2.0 * epsilon) / (sAvgSteps - 1.0)) * i; double x = new InverseCumulativeNormal().value(u[i]); double gf = x * vol * Math.Sqrt(t); double f = fwd * Math.Exp(gf); sig[i] = Math.Pow(leverageFct.localVol(t, f, true), 2.0); } double leverageAvg = new GaussLobattoIntegral(10000, 1E-4).value(new interpolated_volatility(u, sig).value, u.First(), u.Last()) / (1.0 - 2.0 * epsilon); acc.Add(leverageAvg); } volaEstimate_ *= acc.Average(); } }
public cdf_nu_ds(HestonProcess _process, double _nu_0, double _nu_t, double _dt, Discretization _discretization) { process = _process; nu_0 = _nu_0; nu_t = _nu_t; dt = _dt; discretization = _discretization; }
public override void calculate() { // cache lookup for precalculated results for (int i = 0; i < cachedArgs2results_.Count; ++i) { if (cachedArgs2results_[i].first.exercise.type() == arguments_.exercise.type() && cachedArgs2results_[i].first.exercise.dates() == arguments_.exercise.dates()) { PlainVanillaPayoff p1 = arguments_.payoff as PlainVanillaPayoff; PlainVanillaPayoff p2 = cachedArgs2results_[i].first.payoff as PlainVanillaPayoff; if (p1 != null && p1.strike() == p2.strike() && p1.optionType() == p2.optionType()) { Utils.QL_REQUIRE(arguments_.cashFlow.empty(), () => "multiple strikes engine does " + "not work with discrete dividends"); results_ = cachedArgs2results_[i].second; return; } } } HestonProcess process = model_.currentLink().process(); FdmHestonSolver solver = new FdmHestonSolver( new Handle <HestonProcess>(process), getSolverDesc(1.5), schemeDesc_, new Handle <FdmQuantoHelper>(), leverageFct_); double v0 = process.v0(); double spot = process.s0().currentLink().value(); results_.value = solver.valueAt(spot, v0); results_.delta = solver.deltaAt(spot, v0); results_.gamma = solver.gammaAt(spot, v0); results_.theta = solver.thetaAt(spot, v0); cachedArgs2results_ = new InitializedList <Pair <DividendVanillaOption.Arguments, DividendVanillaOption.Results> >(strikes_.Count); StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; for (int i = 0; i < strikes_.Count; ++i) { cachedArgs2results_[i] = new Pair <DividendVanillaOption.Arguments, OneAssetOption.Results>(new DividendVanillaOption.Arguments(), new OneAssetOption.Results()); cachedArgs2results_[i].first.exercise = arguments_.exercise; cachedArgs2results_[i].first.payoff = new PlainVanillaPayoff(payoff.optionType(), strikes_[i]); double d = payoff.strike() / strikes_[i]; cachedArgs2results_[i].second.value = solver.valueAt(spot * d, v0) / d; cachedArgs2results_[i].second.delta = solver.deltaAt(spot * d, v0); cachedArgs2results_[i].second.gamma = solver.gammaAt(spot * d, v0) * d; cachedArgs2results_[i].second.theta = solver.thetaAt(spot * d, v0) / d; } }
protected override double blackVolImpl(double t, double strike) { HestonProcess process = hestonModel_.link.process(); double df = process.riskFreeRate().link.discount(t, true); double div = process.dividendYield().link.discount(t, true); double spotPrice = process.s0().link.value(); double fwd = spotPrice * process.dividendYield().link.discount(t, true) / process.riskFreeRate().link.discount(t, true); var payoff = new PlainVanillaPayoff(fwd > strike ? Option.Type.Put : Option.Type.Call, strike); double kappa = hestonModel_.link.kappa(); double theta = hestonModel_.link.theta(); double rho = hestonModel_.link.rho(); double sigma = hestonModel_.link.sigma(); double v0 = hestonModel_.link.v0(); AnalyticHestonEngine.ComplexLogFormula cpxLogFormula = AnalyticHestonEngine.ComplexLogFormula.Gatheral; AnalyticHestonEngine hestonEnginePtr = null; double?npv = null; int evaluations = 0; AnalyticHestonEngine.doCalculation( df, div, spotPrice, strike, t, kappa, theta, sigma, v0, rho, payoff, integration_, cpxLogFormula, hestonEnginePtr, ref npv, ref evaluations); if (npv <= 0.0) { return(Math.Sqrt(theta)); } Brent solver = new Brent(); solver.setMaxEvaluations(10000); double guess = Math.Sqrt(theta); double accuracy = Const.QL_EPSILON; var f = new ImpliedVolHelper(payoff.optionType(), strike, fwd, t, df, npv.Value); return(solver.solve(f, accuracy, guess, 0.01)); }
public HestonModel(HestonProcess process) : base(5) { process_ = process; arguments_[0] = new ConstantParameter(process.theta(), new PositiveConstraint()); arguments_[1] = new ConstantParameter(process.kappa(), new PositiveConstraint()); arguments_[2] = new ConstantParameter(process.sigma(), new PositiveConstraint()); arguments_[3] = new ConstantParameter(process.rho(), new BoundaryConstraint(-1.0, 1.0)); arguments_[4] = new ConstantParameter(process.v0(), new PositiveConstraint()); generateArguments(); process_.riskFreeRate().registerWith(update); process_.dividendYield().registerWith(update); process_.s0().registerWith(update); }
protected override IPricingEngine controlPricingEngine() { HybridHestonHullWhiteProcess process = process_ as HybridHestonHullWhiteProcess; Utils.QL_REQUIRE(process != null, () => "invalid process"); HestonProcess hestonProcess = process.hestonProcess(); HullWhiteForwardProcess hullWhiteProcess = process.hullWhiteProcess(); HestonModel hestonModel = new HestonModel(hestonProcess); HullWhite hwModel = new HullWhite(hestonProcess.riskFreeRate(), hullWhiteProcess.a(), hullWhiteProcess.sigma()); return(new AnalyticHestonHullWhiteEngine(hestonModel, hwModel, 144)); }
protected override PathPricer <IPath> controlPathPricer() { HybridHestonHullWhiteProcess process = process_ as HybridHestonHullWhiteProcess; Utils.QL_REQUIRE(process != null, () => "invalid process"); HestonProcess hestonProcess = process.hestonProcess(); Utils.QL_REQUIRE(hestonProcess != null, () => "first constituent of the joint stochastic process need to be of type HestonProcess"); Exercise exercise = this.arguments_.exercise; Utils.QL_REQUIRE(exercise.type() == Exercise.Type.European, () => "only european exercise is supported"); double exerciseTime = process.time(exercise.lastDate()); return(new HestonHullWhitePathPricer(exerciseTime, this.arguments_.payoff, process)); }
public override void calculate() { // this is a european option pricer Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option"); // plain vanilla PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff; Utils.QL_REQUIRE(payoff != null, () => "non plain vanilla payoff given"); HestonProcess process = model_.link.process(); double riskFreeDiscount = process.riskFreeRate().link.discount(arguments_.exercise.lastDate()); double dividendDiscount = process.dividendYield().link.discount(arguments_.exercise.lastDate()); double spotPrice = process.s0().link.value(); Utils.QL_REQUIRE(spotPrice > 0.0, () => "negative or null underlying given"); double strikePrice = payoff.strike(); double term = process.time(arguments_.exercise.lastDate()); double?resultsValue = null; doCalculation(riskFreeDiscount, dividendDiscount, spotPrice, strikePrice, term, model_.link.kappa(), model_.link.theta(), model_.link.sigma(), model_.link.v0(), model_.link.rho(), payoff, integration_, cpxLog_, this, ref resultsValue, ref evaluations_); results_.value = resultsValue; }
public FdmHestonOp(FdmMesher mesher, HestonProcess hestonProcess, FdmQuantoHelper quantoHelper = null, LocalVolTermStructure leverageFct = null) { correlationMap_ = new SecondOrderMixedDerivativeOp(0, 1, mesher) .mult(hestonProcess.rho() * hestonProcess.sigma() * mesher.locations(1)); dyMap_ = new FdmHestonVariancePart(mesher, hestonProcess.riskFreeRate().currentLink(), hestonProcess.sigma(), hestonProcess.kappa(), hestonProcess.theta()); dxMap_ = new FdmHestonEquityPart(mesher, hestonProcess.riskFreeRate().currentLink(), hestonProcess.dividendYield().currentLink(), quantoHelper, leverageFct); }
public override void calculate() { // 1. Mesher HestonProcess process = model_.currentLink().process(); double maturity = process.time(arguments_.exercise.lastDate()); // 1.1 The variance mesher int tGridMin = 5; int tGridAvgSteps = Math.Max(tGridMin, tGrid_ / 50); FdmHestonLocalVolatilityVarianceMesher varianceMesher = new FdmHestonLocalVolatilityVarianceMesher(vGrid_, process, leverageFct_, maturity, tGridAvgSteps); // 1.2 the equity mesher StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; double?xMin = null; double?xMax = null; if (arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut) { xMin = Math.Log(arguments_.barrier.Value); } if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { xMax = Math.Log(arguments_.barrier.Value); } Fdm1dMesher equityMesher = new FdmBlackScholesMesher(xGrid_, FdmBlackScholesMesher.processHelper(process.s0(), process.dividendYield(), process.riskFreeRate(), varianceMesher.volaEstimate()), maturity, payoff.strike(), xMin, xMax, 0.0001, 1.5, new Pair <double?, double?>(), arguments_.cashFlow); FdmMesher mesher = new FdmMesherComposite(equityMesher, varianceMesher); // 2. Calculator StrikedTypePayoff rebatePayoff = new CashOrNothingPayoff(Option.Type.Call, 0.0, arguments_.rebate.Value); FdmInnerValueCalculator calculator = new FdmLogInnerValue(rebatePayoff, mesher, 0); // 3. Step conditions Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "only european style option are supported"); FdmStepConditionComposite conditions = FdmStepConditionComposite.vanillaComposite( arguments_.cashFlow, arguments_.exercise, mesher, calculator, process.riskFreeRate().currentLink().referenceDate(), process.riskFreeRate().currentLink().dayCounter()); // 4. Boundary conditions FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet(); if (arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut) { boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0, FdmDirichletBoundary.Side.Lower)); } if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0, FdmDirichletBoundary.Side.Upper)); } // 5. Solver FdmSolverDesc solverDesc = new FdmSolverDesc(); solverDesc.mesher = mesher; solverDesc.bcSet = boundaries; solverDesc.condition = conditions; solverDesc.calculator = calculator; solverDesc.maturity = maturity; solverDesc.dampingSteps = dampingSteps_; solverDesc.timeSteps = tGrid_; FdmHestonSolver solver = new FdmHestonSolver( new Handle <HestonProcess>(process), solverDesc, schemeDesc_, new Handle <FdmQuantoHelper>(), leverageFct_); double spot = process.s0().currentLink().value(); results_.value = solver.valueAt(spot, process.v0()); results_.delta = solver.deltaAt(spot, process.v0()); results_.gamma = solver.gammaAt(spot, process.v0()); results_.theta = solver.thetaAt(spot, process.v0()); }
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; }
public cdf_nu_ds_minus_x(double _x0, HestonProcess _process, double _nu_0, double _nu_t, double _dt, Discretization _discretization) { cdf_nu = new cdf_nu_ds(_process, _nu_0, _nu_t, _dt, _discretization); x0 = _x0; }
public override void calculate() { // 1. Mesher HestonProcess process = model_.currentLink().process(); double maturity = process.time(arguments_.exercise.lastDate()); // 1.1 Variance Mesher int tGridMin = 5; int tGridAvgSteps = Math.Max(tGridMin, tGrid_ / 50); FdmHestonLocalVolatilityVarianceMesher varianceMesher = new FdmHestonLocalVolatilityVarianceMesher(vGrid_, process, leverageFct_, maturity, tGridAvgSteps); // 1.2 Equity Mesher StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; double?xMin = null; double?xMax = null; if (arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut) { xMin = Math.Log(arguments_.barrier.Value); } if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { xMax = Math.Log(arguments_.barrier.Value); } Fdm1dMesher equityMesher = new FdmBlackScholesMesher(xGrid_, FdmBlackScholesMesher.processHelper(process.s0(), process.dividendYield(), process.riskFreeRate(), varianceMesher.volaEstimate()), maturity, payoff.strike(), xMin, xMax, 0.0001, 1.5, new Pair <double?, double?>(), arguments_.cashFlow); FdmMesher mesher = new FdmMesherComposite(equityMesher, varianceMesher); // 2. Calculator FdmInnerValueCalculator calculator = new FdmLogInnerValue(payoff, mesher, 0); // 3. Step conditions List <IStepCondition <Vector> > stepConditions = new List <IStepCondition <Vector> >(); List <List <double> > stoppingTimes = new List <List <double> >(); // 3.1 Step condition if discrete dividends FdmDividendHandler dividendCondition = new FdmDividendHandler(arguments_.cashFlow, mesher, process.riskFreeRate().currentLink().referenceDate(), process.riskFreeRate().currentLink().dayCounter(), 0); if (!arguments_.cashFlow.empty()) { stepConditions.Add(dividendCondition); stoppingTimes.Add(dividendCondition.dividendTimes()); } Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "only european style option are supported"); FdmStepConditionComposite conditions = new FdmStepConditionComposite(stoppingTimes, stepConditions); // 4. Boundary conditions FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet(); if (arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.DownOut) { boundaries.Add( new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0, FdmDirichletBoundary.Side.Lower)); } if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut) { boundaries.Add( new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0, FdmDirichletBoundary.Side.Upper)); } // 5. Solver FdmSolverDesc solverDesc = new FdmSolverDesc(); solverDesc.mesher = mesher; solverDesc.bcSet = boundaries; solverDesc.condition = conditions; solverDesc.calculator = calculator; solverDesc.maturity = maturity; solverDesc.dampingSteps = dampingSteps_; solverDesc.timeSteps = tGrid_; FdmHestonSolver solver = new FdmHestonSolver( new Handle <HestonProcess>(process), solverDesc, schemeDesc_, new Handle <FdmQuantoHelper>(), leverageFct_); double spot = process.s0().currentLink().value(); results_.value = solver.valueAt(spot, process.v0()); results_.delta = solver.deltaAt(spot, process.v0()); results_.gamma = solver.gammaAt(spot, process.v0()); results_.theta = solver.thetaAt(spot, process.v0()); // 6. Calculate vanilla option and rebate for in-barriers if (arguments_.barrierType == Barrier.Type.DownIn || arguments_.barrierType == Barrier.Type.UpIn) { // Cast the payoff StrikedTypePayoff castedPayoff = arguments_.payoff as StrikedTypePayoff; // Calculate the vanilla option DividendVanillaOption vanillaOption = new DividendVanillaOption(castedPayoff, arguments_.exercise, dividendCondition.dividendDates(), dividendCondition.dividends()); vanillaOption.setPricingEngine( new FdHestonVanillaEngine( model_, tGrid_, xGrid_, vGrid_, dampingSteps_, schemeDesc_)); // Calculate the rebate value DividendBarrierOption rebateOption = new DividendBarrierOption(arguments_.barrierType, arguments_.barrier.Value, arguments_.rebate.Value, castedPayoff, arguments_.exercise, dividendCondition.dividendDates(), dividendCondition.dividends()); int xGridMin = 20; int vGridMin = 10; int rebateDampingSteps = (dampingSteps_ > 0) ? Math.Min(1, dampingSteps_ / 2) : 0; rebateOption.setPricingEngine(new FdHestonRebateEngine( model_, tGrid_, Math.Max(xGridMin, xGrid_ / 4), Math.Max(vGridMin, vGrid_ / 4), rebateDampingSteps, schemeDesc_)); results_.value = vanillaOption.NPV() + rebateOption.NPV() - results_.value; results_.delta = vanillaOption.delta() + rebateOption.delta() - results_.delta; results_.gamma = vanillaOption.gamma() + rebateOption.gamma() - results_.gamma; results_.theta = vanillaOption.theta() + rebateOption.theta() - results_.theta; } }
public FdmSolverDesc getSolverDesc(double x) { // 1. Mesher HestonProcess process = model_.currentLink().process(); double maturity = process.time(arguments_.exercise.lastDate()); // 1.1 The variance mesher int tGridMin = 5; FdmHestonVarianceMesher varianceMesher = new FdmHestonVarianceMesher(vGrid_, process, maturity, Math.Max(tGridMin, tGrid_ / 50)); // 1.2 The equity mesher StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff; Fdm1dMesher equityMesher; if (strikes_.empty()) { equityMesher = new FdmBlackScholesMesher( xGrid_, FdmBlackScholesMesher.processHelper( process.s0(), process.dividendYield(), process.riskFreeRate(), varianceMesher.volaEstimate()), maturity, payoff.strike(), null, null, 0.0001, x, new Pair <double?, double?>(payoff.strike(), 0.1), arguments_.cashFlow); } else { Utils.QL_REQUIRE(arguments_.cashFlow.empty(), () => "multiple strikes engine " + "does not work with discrete dividends"); equityMesher = new FdmBlackScholesMultiStrikeMesher( xGrid_, FdmBlackScholesMesher.processHelper( process.s0(), process.dividendYield(), process.riskFreeRate(), varianceMesher.volaEstimate()), maturity, strikes_, 0.0001, x, new Pair <double?, double?>(payoff.strike(), 0.075)); } FdmMesher mesher = new FdmMesherComposite(equityMesher, varianceMesher); // 2. Calculator FdmInnerValueCalculator calculator = new FdmLogInnerValue(arguments_.payoff, mesher, 0); // 3. Step conditions FdmStepConditionComposite conditions = FdmStepConditionComposite.vanillaComposite( arguments_.cashFlow, arguments_.exercise, mesher, calculator, process.riskFreeRate().currentLink().referenceDate(), process.riskFreeRate().currentLink().dayCounter()); // 4. Boundary conditions FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet(); // 5. Solver FdmSolverDesc solverDesc = new FdmSolverDesc(); solverDesc.mesher = mesher; solverDesc.bcSet = boundaries; solverDesc.condition = conditions; solverDesc.calculator = calculator; solverDesc.maturity = maturity; solverDesc.dampingSteps = dampingSteps_; solverDesc.timeSteps = tGrid_; return(solverDesc); }