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 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;
            }
        }
예제 #3
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));
        }
예제 #4
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);
        }
예제 #5
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;
        }
예제 #6
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 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);
        }