// 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);
            }
Exemple #3
0
        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;
            }
        }
Exemple #9
0
        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));
        }
Exemple #10
0
        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);
        }
Exemple #11
0
        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));
        }
Exemple #12
0
        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));
        }
Exemple #13
0
        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;
        }
Exemple #14
0
        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);
        }
Exemple #15
0
        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);
        }