Ejemplo n.º 1
0
        public static GeneralizedBlackScholesProcess clone(GeneralizedBlackScholesProcess process, SimpleQuote volQuote)
        {
            Handle <Quote> stateVariable = process.stateVariable();
            Handle <YieldTermStructure> dividendYield = process.dividendYield();
            Handle <YieldTermStructure> riskFreeRate  = process.riskFreeRate();

            Handle <BlackVolTermStructure> blackVol = process.blackVolatility();
            var volatility = new Handle <BlackVolTermStructure>(new BlackConstantVol(blackVol.link.referenceDate(),
                                                                                     blackVol.link.calendar(), new Handle <Quote>(volQuote),
                                                                                     blackVol.link.dayCounter()));

            return(new GeneralizedBlackScholesProcess(stateVariable, dividendYield, riskFreeRate, volatility));
        }
        // McSimulation implementation
        protected override TimeGrid timeGrid()
        {
            Date          referenceDate = process_.riskFreeRate().link.referenceDate();
            DayCounter    voldc         = process_.blackVolatility().link.dayCounter();
            List <double> fixingTimes   = new  InitializedList <double>(arguments_.fixingDates.Count);

            for (int i = 0; i < arguments_.fixingDates.Count; i++)
            {
                if (arguments_.fixingDates[i] >= referenceDate)
                {
                    double t = voldc.yearFraction(referenceDate,
                                                  arguments_.fixingDates[i]);
                    fixingTimes.Add(t);
                }
            }
            // handle here maxStepsPerYear
            return(new TimeGrid(fixingTimes.Last(), fixingTimes.Count));
        }
Ejemplo n.º 3
0
        protected void setGridLimits(double center, double t)
        {
            Utils.QL_REQUIRE(center > 0.0, () => "negative or null underlying given");
            Utils.QL_REQUIRE(t > 0.0, () => "negative or zero residual time");
            center_ = center;
            int newGridPoints = safeGridPoints(gridPoints_, t);

            if (newGridPoints > intrinsicValues_.size())
            {
                intrinsicValues_ = new SampledCurve(newGridPoints);
            }

            double volSqrtTime = Math.Sqrt(process_.blackVolatility().link.blackVariance(t, center_));

            // the prefactor fine tunes performance at small volatilities
            double prefactor    = 1.0 + 0.02 / volSqrtTime;
            double minMaxFactor = Math.Exp(4.0 * prefactor * volSqrtTime);

            sMin_ = center_ / minMaxFactor; // underlying grid min value
            sMax_ = center_ * minMaxFactor; // underlying grid max value
        }
Ejemplo n.º 4
0
 public FdmBlackScholesOp(FdmMesher mesher,
                          GeneralizedBlackScholesProcess bsProcess,
                          double strike,
                          bool localVol = false,
                          double?illegalLocalVolOverwrite = null,
                          int direction = 0,
                          FdmQuantoHelper quantoHelper = null)
 {
     mesher_   = mesher;
     rTS_      = bsProcess.riskFreeRate().currentLink();
     qTS_      = bsProcess.dividendYield().currentLink();
     volTS_    = bsProcess.blackVolatility().currentLink();
     localVol_ = (localVol) ? bsProcess.localVolatility().currentLink()
              : null;
     x_      = (localVol) ? new Vector(Vector.Exp(mesher.locations(direction))) : null;
     dxMap_  = new FirstDerivativeOp(direction, mesher);
     dxxMap_ = new SecondDerivativeOp(direction, mesher);
     mapT_   = new TripleBandLinearOp(direction, mesher);
     strike_ = strike;
     illegalLocalVolOverwrite_ = illegalLocalVolOverwrite;
     direction_    = direction;
     quantoHelper_ = quantoHelper;
 }
Ejemplo n.º 5
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option");

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Utils.QL_REQUIRE(payoff != null, () => "not an European Option");

            double variance = process_.blackVolatility().link.blackVariance(arguments_.exercise.lastDate(), payoff.strike());

            double dividendDiscount = process_.dividendYield().link.discount(arguments_.exercise.lastDate());
            double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate());
            double drift            = Math.Log(dividendDiscount / riskFreeDiscount) - 0.5 * variance;

            Integrand       f          = new Integrand(arguments_.payoff, process_.stateVariable().link.value(), drift, variance);
            SegmentIntegral integrator = new SegmentIntegral(5000);

            double infinity = 10.0 * Math.Sqrt(variance);

            results_.value = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate()) /
                             Math.Sqrt(2.0 * Math.PI * variance) *
                             integrator.value(f.value, drift - infinity, drift + infinity);
        }
Ejemplo n.º 6
0
 private double volatility()
 {
     return(process_.blackVolatility().link.blackVol(residualTime(), strike()));
 }
Ejemplo n.º 7
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

            Utils.QL_REQUIRE(ex != null, () => "non-American exercise given");

            Utils.QL_REQUIRE(!ex.payoffAtExpiry(), () => "payoff at expiry not handled");

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given");

            double variance         = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike());
            double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate());
            double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate());
            double spot             = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");

            double          forwardPrice = spot * dividendDiscount / riskFreeDiscount;
            BlackCalculator black        = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount);

            if (dividendDiscount >= 1.0 && payoff.optionType() == Option.Type.Call)
            {
                // early exercise never optimal
                results_.value        = black.value();
                results_.delta        = black.delta(spot);
                results_.deltaForward = black.deltaForward();
                results_.elasticity   = black.elasticity(spot);
                results_.gamma        = black.gamma(spot);

                DayCounter rfdc  = process_.riskFreeRate().link.dayCounter();
                DayCounter divdc = process_.dividendYield().link.dayCounter();
                DayCounter voldc = process_.blackVolatility().link.dayCounter();
                double     t     = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate());
                results_.rho = black.rho(t);

                t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate());
                results_.dividendRho = black.dividendRho(t);

                t                    = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate());
                results_.vega        = black.vega(t);
                results_.theta       = black.theta(spot, t);
                results_.thetaPerDay = black.thetaPerDay(spot, t);

                results_.strikeSensitivity  = black.strikeSensitivity();
                results_.itmCashProbability = black.itmCashProbability();
            }
            else
            {
                // early exercise can be optimal
                CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
                NormalDistribution           normalDist    = new NormalDistribution();

                double tolerance = 1e-6;
                double Sk        = BaroneAdesiWhaleyApproximationEngine.criticalPrice(payoff, riskFreeDiscount, dividendDiscount,
                                                                                      variance, tolerance);

                double forwardSk = Sk * dividendDiscount / riskFreeDiscount;

                double alpha = -2.0 * Math.Log(riskFreeDiscount) / (variance);
                double beta  = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / (variance);
                double h     = 1 - riskFreeDiscount;
                double phi   = 0;
                switch (payoff.optionType())
                {
                case Option.Type.Call:
                    phi = 1;
                    break;

                case Option.Type.Put:
                    phi = -1;
                    break;

                default:
                    Utils.QL_FAIL("invalid option type");
                    break;
                }
                //it can throw: to be fixed
                // FLOATING_POINT_EXCEPTION
                double temp_root    = Math.Sqrt((beta - 1) * (beta - 1) + (4 * alpha) / h);
                double lambda       = (-(beta - 1) + phi * temp_root) / 2;
                double lambda_prime = -phi * alpha / (h * h * temp_root);

                double black_Sk = Utils.blackFormula(payoff.optionType(), payoff.strike(), forwardSk, Math.Sqrt(variance)) *
                                  riskFreeDiscount;
                double hA = phi * (Sk - payoff.strike()) - black_Sk;

                double d1_Sk = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance) / Math.Sqrt(variance);
                double d2_Sk = d1_Sk - Math.Sqrt(variance);
                double part1 = forwardSk * normalDist.value(d1_Sk) / (alpha * Math.Sqrt(variance));
                double part2 = -phi *forwardSk *cumNormalDist.value(phi *d1_Sk) * Math.Log(dividendDiscount) /
                               Math.Log(riskFreeDiscount);

                double part3 = +phi *payoff.strike() * cumNormalDist.value(phi * d2_Sk);

                double V_E_h = part1 + part2 + part3;

                double b = (1 - h) * alpha * lambda_prime / (2 * (2 * lambda + beta - 1));
                double c = -((1 - h) * alpha / (2 * lambda + beta - 1)) *
                           (V_E_h / (hA) + 1 / h + lambda_prime / (2 * lambda + beta - 1));
                double temp_spot_ratio = Math.Log(spot / Sk);
                double chi             = temp_spot_ratio * (b * temp_spot_ratio + c);

                if (phi * (Sk - spot) > 0)
                {
                    results_.value = black.value() + hA * Math.Pow((spot / Sk), lambda) / (1 - chi);
                }
                else
                {
                    results_.value = phi * (spot - payoff.strike());
                }

                double temp_chi_prime   = (2 * b / spot) * Math.Log(spot / Sk);
                double chi_prime        = temp_chi_prime + c / spot;
                double chi_double_prime = 2 * b / (spot * spot) - temp_chi_prime / spot - c / (spot * spot);
                results_.delta = phi * dividendDiscount * cumNormalDist.value(phi * d1_Sk) +
                                 (lambda / (spot * (1 - chi)) + chi_prime / ((1 - chi) * (1 - chi))) *
                                 (phi * (Sk - payoff.strike()) - black_Sk) * Math.Pow((spot / Sk), lambda);

                results_.gamma = phi * dividendDiscount * normalDist.value(phi * d1_Sk) / (spot * Math.Sqrt(variance)) +
                                 (2 * lambda * chi_prime / (spot * (1 - chi) * (1 - chi)) +
                                  2 * chi_prime * chi_prime / ((1 - chi) * (1 - chi) * (1 - chi)) +
                                  chi_double_prime / ((1 - chi) * (1 - chi)) +
                                  lambda * (1 - lambda) / (spot * spot * (1 - chi))) *
                                 (phi * (Sk - payoff.strike()) - black_Sk) * Math.Pow((spot / Sk), lambda);
            } // end of "early exercise can be optimal"
        }
Ejemplo n.º 8
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

            Utils.QL_REQUIRE(ex != null, () => "non-American exercise given");

            Utils.QL_REQUIRE(!ex.payoffAtExpiry(), () => "payoff at expiry not handled");

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-striked payoff given");

            double variance         = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike());
            double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate());
            double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate());
            double spot             = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");
            double          forwardPrice = spot * dividendDiscount / riskFreeDiscount;
            BlackCalculator black        = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount);

            if (dividendDiscount >= 1.0 && payoff.optionType() == Option.Type.Call)
            {
                // early exercise never optimal
                results_.value        = black.value();
                results_.delta        = black.delta(spot);
                results_.deltaForward = black.deltaForward();
                results_.elasticity   = black.elasticity(spot);
                results_.gamma        = black.gamma(spot);

                DayCounter rfdc  = process_.riskFreeRate().link.dayCounter();
                DayCounter divdc = process_.dividendYield().link.dayCounter();
                DayCounter voldc = process_.blackVolatility().link.dayCounter();
                double     t     = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate());
                results_.rho = black.rho(t);

                t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate());
                results_.dividendRho = black.dividendRho(t);

                t                    = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate());
                results_.vega        = black.vega(t);
                results_.theta       = black.theta(spot, t);
                results_.thetaPerDay = black.thetaPerDay(spot, t);

                results_.strikeSensitivity  = black.strikeSensitivity();
                results_.itmCashProbability = black.itmCashProbability();
            }
            else
            {
                // early exercise can be optimal
                CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
                double tolerance = 1e-6;
                double Sk        = criticalPrice(payoff, riskFreeDiscount,
                                                 dividendDiscount, variance, tolerance);
                double forwardSk = Sk * dividendDiscount / riskFreeDiscount;
                double d1        = (Math.Log(forwardSk / payoff.strike()) + 0.5 * variance)
                                   / Math.Sqrt(variance);
                double n = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / variance;
                double K = (!Utils.close(riskFreeDiscount, 1.0, 1000))
                       ? -2.0 * Math.Log(riskFreeDiscount)
                           / (variance * (1.0 - riskFreeDiscount))
                       : 2.0 / variance;
                double Q, a;
                switch (payoff.optionType())
                {
                case Option.Type.Call:
                    Q = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0;
                    a = (Sk / Q) * (1.0 - dividendDiscount * cumNormalDist.value(d1));
                    if (spot < Sk)
                    {
                        results_.value = black.value() +
                                         a * Math.Pow((spot / Sk), Q);
                    }
                    else
                    {
                        results_.value = spot - payoff.strike();
                    }
                    break;

                case Option.Type.Put:
                    Q = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * K)) / 2.0;
                    a = -(Sk / Q) *
                        (1.0 - dividendDiscount * cumNormalDist.value(-d1));
                    if (spot > Sk)
                    {
                        results_.value = black.value() +
                                         a * Math.Pow((spot / Sk), Q);
                    }
                    else
                    {
                        results_.value = payoff.strike() - spot;
                    }
                    break;

                default:
                    Utils.QL_FAIL("unknown option type");
                    break;
                }
            } // end of "early exercise can be optimal"
        }
        public override void calculate()
        {
            /* this engine cannot really check for the averageType==Geometric
             * since it can be used as control variate for the Arithmetic version
             * QL_REQUIRE(arguments_.averageType == Average::Geometric,"not a geometric average option")
             */
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European Option");

            double runningLog;
            int    pastFixings;

            if (arguments_.averageType == Average.Type.Geometric)
            {
                Utils.QL_REQUIRE(arguments_.runningAccumulator > 0.0, () =>
                                 "positive running product required: " + arguments_.runningAccumulator + " not allowed");

                runningLog  = Math.Log(arguments_.runningAccumulator.GetValueOrDefault());
                pastFixings = arguments_.pastFixings.GetValueOrDefault();
            }
            else
            {
                // it is being used as control variate
                runningLog  = 1.0;
                pastFixings = 0;
            }

            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given");

            Date          referenceDate = process_.riskFreeRate().link.referenceDate();
            DayCounter    rfdc          = process_.riskFreeRate().link.dayCounter();
            DayCounter    divdc         = process_.dividendYield().link.dayCounter();
            DayCounter    voldc         = process_.blackVolatility().link.dayCounter();
            List <double> fixingTimes   = new List <double>();
            int           i;

            for (i = 0; i < arguments_.fixingDates.Count; i++)
            {
                if (arguments_.fixingDates[i] >= referenceDate)
                {
                    double t = voldc.yearFraction(referenceDate, arguments_.fixingDates[i]);
                    fixingTimes.Add(t);
                }
            }

            int    remainingFixings = fixingTimes.Count;
            int    numberOfFixings  = pastFixings + remainingFixings;
            double N = numberOfFixings;

            double pastWeight   = pastFixings / N;
            double futureWeight = 1.0 - pastWeight;

            double timeSum = 0;

            fixingTimes.ForEach((ii, vv) => timeSum += fixingTimes[ii]);

            double vola = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), payoff.strike());
            double temp = 0.0;

            for (i = pastFixings + 1; i < numberOfFixings; i++)
            {
                temp += fixingTimes[i - pastFixings - 1] * (N - i);
            }
            double variance   = vola * vola / N / N * (timeSum + 2.0 * temp);
            double dsigG_dsig = Math.Sqrt((timeSum + 2.0 * temp)) / N;
            double sigG       = vola * dsigG_dsig;
            double dmuG_dsig  = -(vola * timeSum) / N;

            Date   exDate       = arguments_.exercise.lastDate();
            double dividendRate = process_.dividendYield().link.
                                  zeroRate(exDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate();
            double riskFreeRate = process_.riskFreeRate().link.
                                  zeroRate(exDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate();
            double nu = riskFreeRate - dividendRate - 0.5 * vola * vola;

            double s = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(s > 0.0, () => "positive underlying value required");

            int    M            = (pastFixings == 0 ? 1 : pastFixings);
            double muG          = pastWeight * runningLog / M + futureWeight * Math.Log(s) + nu * timeSum / N;
            double forwardPrice = Math.Exp(muG + variance / 2.0);

            double riskFreeDiscount = process_.riskFreeRate().link.discount(arguments_.exercise.lastDate());

            BlackCalculator black = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount);

            results_.value = black.value();
            results_.delta = futureWeight * black.delta(forwardPrice) * forwardPrice / s;
            results_.gamma = forwardPrice * futureWeight / (s * s)
                             * (black.gamma(forwardPrice) * futureWeight * forwardPrice
                                - pastWeight * black.delta(forwardPrice));

            double Nx_1, nx_1;
            CumulativeNormalDistribution CND = new CumulativeNormalDistribution();
            NormalDistribution           ND  = new NormalDistribution();

            if (sigG > Const.QL_EPSILON)
            {
                double x_1 = (muG - Math.Log(payoff.strike()) + variance) / sigG;
                Nx_1 = CND.value(x_1);
                nx_1 = ND.value(x_1);
            }
            else
            {
                Nx_1 = (muG > Math.Log(payoff.strike()) ? 1.0 : 0.0);
                nx_1 = 0.0;
            }
            results_.vega = forwardPrice * riskFreeDiscount *
                            ((dmuG_dsig + sigG * dsigG_dsig) * Nx_1 + nx_1 * dsigG_dsig);

            if (payoff.optionType() == Option.Type.Put)
            {
                results_.vega -= riskFreeDiscount * forwardPrice *
                                 (dmuG_dsig + sigG * dsigG_dsig);
            }

            double tRho = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(),
                                            arguments_.exercise.lastDate());

            results_.rho = black.rho(tRho) * timeSum / (N * tRho)
                           - (tRho - timeSum / N) * results_.value;

            double tDiv = divdc.yearFraction(
                process_.dividendYield().link.referenceDate(),
                arguments_.exercise.lastDate());

            results_.dividendRho = black.dividendRho(tDiv) * timeSum / (N * tDiv);

            results_.strikeSensitivity = black.strikeSensitivity();

            results_.theta = Utils.blackScholesTheta(process_,
                                                     results_.value.GetValueOrDefault(),
                                                     results_.delta.GetValueOrDefault(),
                                                     results_.gamma.GetValueOrDefault());
        }
Ejemplo n.º 10
0
        public override void calculate()
        {
            DayCounter rfdc   = process_.riskFreeRate().link.dayCounter();
            DayCounter divdc  = process_.dividendYield().link.dayCounter();
            DayCounter voldc  = process_.blackVolatility().link.dayCounter();
            Calendar   volcal = process_.blackVolatility().link.calendar();

            double s0 = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(s0 > 0.0, () => "negative or null underlying given");
            double v             = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), s0);
            Date   maturityDate  = arguments_.exercise.lastDate();
            double r             = process_.riskFreeRate().link.zeroRate(maturityDate, rfdc, Compounding.Continuous, Frequency.NoFrequency).rate();
            double q             = process_.dividendYield().link.zeroRate(maturityDate, divdc, Compounding.Continuous, Frequency.NoFrequency).rate();
            Date   referenceDate = process_.riskFreeRate().link.referenceDate();

            // binomial trees with constant coefficient
            var flatRiskFree  = new Handle <YieldTermStructure>(new FlatForward(referenceDate, r, rfdc));
            var flatDividends = new Handle <YieldTermStructure>(new FlatForward(referenceDate, q, divdc));
            var flatVol       = new Handle <BlackVolTermStructure>(new BlackConstantVol(referenceDate, volcal, v, voldc));

            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given");

            double maturity = rfdc.yearFraction(referenceDate, maturityDate);

            StochasticProcess1D bs =
                new GeneralizedBlackScholesProcess(process_.stateVariable(), flatDividends, flatRiskFree, flatVol);

            TimeGrid grid = new TimeGrid(maturity, timeSteps_);

            T tree = FastActivator <T> .Create().factory(bs, maturity, timeSteps_, payoff.strike());

            BlackScholesLattice <T> lattice = new BlackScholesLattice <T>(tree, r, maturity, timeSteps_);

            DiscretizedVanillaOption option = new DiscretizedVanillaOption(arguments_, process_, grid);

            option.initialize(lattice, maturity);

            // Partial derivatives calculated from various points in the
            // binomial tree (Odegaard)

            // Rollback to third-last step, and get underlying price (s2) &
            // option values (p2) at this point
            option.rollback(grid[2]);
            Vector va2 = new Vector(option.values());

            Utils.QL_REQUIRE(va2.size() == 3, () => "Expect 3 nodes in grid at second step");
            double p2h = va2[2];                   // high-price
            double s2  = lattice.underlying(2, 2); // high price

            // Rollback to second-last step, and get option value (p1) at
            // this point
            option.rollback(grid[1]);
            Vector va = new Vector(option.values());

            Utils.QL_REQUIRE(va.size() == 2, () => "Expect 2 nodes in grid at first step");
            double p1 = va[1];

            // Finally, rollback to t=0
            option.rollback(0.0);
            double p0 = option.presentValue();
            double s1 = lattice.underlying(1, 1);

            // Calculate partial derivatives
            double delta0 = (p1 - p0) / (s1 - s0);  // dp/ds
            double delta1 = (p2h - p1) / (s2 - s1); // dp/ds

            // Store results
            results_.value = p0;
            results_.delta = delta0;
            results_.gamma = 2.0 * (delta1 - delta0) / (s2 - s0); //d(delta)/ds
            results_.theta = Utils.blackScholesTheta(process_,
                                                     results_.value.GetValueOrDefault(),
                                                     results_.delta.GetValueOrDefault(),
                                                     results_.gamma.GetValueOrDefault());
        }
        public FdmBlackScholesMultiStrikeMesher(int size,
                                                GeneralizedBlackScholesProcess process,
                                                double maturity,
                                                List <double> strikes,
                                                double eps         = 0.0001,
                                                double scaleFactor = 1.5,
                                                Pair <double?, double?> cPoint
                                                = null)
            : base(size)
        {
            double spot = process.x0();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");

            double d = process.dividendYield().currentLink().discount(maturity)
                       / process.riskFreeRate().currentLink().discount(maturity);
            double minStrike = strikes.Min();
            double maxStrike = strikes.Max();

            double Fmin = spot * spot / maxStrike * d;
            double Fmax = spot * spot / minStrike * d;

            Utils.QL_REQUIRE(Fmin > 0.0, () => "negative forward given");

            // Set the grid boundaries
            double normInvEps = new InverseCumulativeNormal().value(1 - eps);
            double sigmaSqrtTmin
                = process.blackVolatility().currentLink().blackVol(maturity, minStrike)
                  * Math.Sqrt(maturity);
            double sigmaSqrtTmax
                = process.blackVolatility().currentLink().blackVol(maturity, maxStrike)
                  * Math.Sqrt(maturity);

            double xMin
                = Math.Min(0.8 * Math.Log(0.8 * spot * spot / maxStrike),
                           Math.Log(Fmin) - sigmaSqrtTmin * normInvEps * scaleFactor
                           - sigmaSqrtTmin * sigmaSqrtTmin / 2.0);
            double xMax
                = Math.Max(1.2 * Math.Log(0.8 * spot * spot / minStrike),
                           Math.Log(Fmax) + sigmaSqrtTmax * normInvEps * scaleFactor
                           - sigmaSqrtTmax * sigmaSqrtTmax / 2.0);

            Fdm1dMesher helper;

            if (cPoint.first != null &&
                Math.Log(cPoint.first.Value) >= xMin && Math.Log(cPoint.first.Value) <= xMax)
            {
                helper = new Concentrating1dMesher(xMin, xMax, size,
                                                   new Pair <double?, double?>(Math.Log(cPoint.first.Value), cPoint.second));
            }
            else
            {
                helper = new Uniform1dMesher(xMin, xMax, size);
            }

            locations_ = helper.locations();
            for (int i = 0; i < locations_.Count; ++i)
            {
                dplus_[i]  = helper.dplus(i);
                dminus_[i] = helper.dminus(i);
            }
        }
Ejemplo n.º 12
0
        public FdmBlackScholesMesher(int size,
                                     GeneralizedBlackScholesProcess process,
                                     double maturity, double strike,
                                     double?xMinConstraint = null,
                                     double?xMaxConstraint = null,
                                     double eps            = 0.0001,
                                     double scaleFactor    = 1.5,
                                     Pair <double?, double?> cPoint
                                     = null,
                                     DividendSchedule dividendSchedule = null,
                                     FdmQuantoHelper fdmQuantoHelper   = null,
                                     double spotAdjustment             = 0.0)
            : base(size)
        {
            double S = process.x0();

            Utils.QL_REQUIRE(S > 0.0, () => "negative or null underlying given");

            dividendSchedule = dividendSchedule == null ? new DividendSchedule() : dividendSchedule;
            List <pair_double> intermediateSteps = new List <pair_double>();

            for (int i = 0; i < dividendSchedule.Count &&
                 process.time(dividendSchedule[i].date()) <= maturity; ++i)
            {
                intermediateSteps.Add(
                    new pair_double(
                        process.time(dividendSchedule[i].date()),
                        dividendSchedule[i].amount()
                        ));
            }

            int intermediateTimeSteps = (int)Math.Max(2, 24.0 * maturity);

            for (int i = 0; i < intermediateTimeSteps; ++i)
            {
                intermediateSteps.Add(
                    new pair_double((i + 1) * (maturity / intermediateTimeSteps), 0.0));
            }

            intermediateSteps.Sort();

            Handle <YieldTermStructure> rTS = process.riskFreeRate();
            Handle <YieldTermStructure> qTS = fdmQuantoHelper != null
                                          ? new Handle <YieldTermStructure>(
                new QuantoTermStructure(process.dividendYield(),
                                        process.riskFreeRate(),
                                        new Handle <YieldTermStructure>(fdmQuantoHelper.foreignTermStructure()),
                                        process.blackVolatility(),
                                        strike,
                                        new Handle <BlackVolTermStructure>(fdmQuantoHelper.fxVolatilityTermStructure()),
                                        fdmQuantoHelper.exchRateATMlevel(),
                                        fdmQuantoHelper.equityFxCorrelation()))
                                          : process.dividendYield();

            double lastDivTime = 0.0;
            double fwd = S + spotAdjustment;
            double mi = fwd, ma = fwd;

            for (int i = 0; i < intermediateSteps.Count; ++i)
            {
                double divTime   = intermediateSteps[i].first;
                double divAmount = intermediateSteps[i].second;

                fwd = fwd / rTS.currentLink().discount(divTime) * rTS.currentLink().discount(lastDivTime)
                      * qTS.currentLink().discount(divTime) / qTS.currentLink().discount(lastDivTime);

                mi = Math.Min(mi, fwd); ma = Math.Max(ma, fwd);

                fwd -= divAmount;

                mi = Math.Min(mi, fwd); ma = Math.Max(ma, fwd);

                lastDivTime = divTime;
            }

            // Set the grid boundaries
            double normInvEps = new InverseCumulativeNormal().value(1 - eps);
            double sigmaSqrtT
                = process.blackVolatility().currentLink().blackVol(maturity, strike)
                  * Math.Sqrt(maturity);

            double?xMin = Math.Log(mi) - sigmaSqrtT * normInvEps * scaleFactor;
            double?xMax = Math.Log(ma) + sigmaSqrtT * normInvEps * scaleFactor;

            if (xMinConstraint != null)
            {
                xMin = xMinConstraint;
            }
            if (xMaxConstraint != null)
            {
                xMax = xMaxConstraint;
            }

            Fdm1dMesher helper;

            if (cPoint != null &&
                cPoint.first != null &&
                Math.Log(cPoint.first.Value) >= xMin && Math.Log(cPoint.first.Value) <= xMax)
            {
                helper = new Concentrating1dMesher(xMin.Value, xMax.Value, size,
                                                   new Pair <double?, double?>(Math.Log(cPoint.first.Value), cPoint.second));
            }
            else
            {
                helper = new Uniform1dMesher(xMin.Value, xMax.Value, size);
            }

            locations_ = helper.locations();
            for (int i = 0; i < locations_.Count; ++i)
            {
                dplus_[i]  = helper.dplus(i);
                dminus_[i] = helper.dminus(i);
            }
        }
Ejemplo n.º 13
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

            Utils.QL_REQUIRE(ex != null, () => "non-American exercise given");

            Utils.QL_REQUIRE(!ex.payoffAtExpiry(), () => "payoff at expiry not handled");

            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given");

            double variance         = process_.blackVolatility().link.blackVariance(ex.lastDate(), payoff.strike());
            double dividendDiscount = process_.dividendYield().link.discount(ex.lastDate());
            double riskFreeDiscount = process_.riskFreeRate().link.discount(ex.lastDate());

            double spot = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(spot > 0.0, () => "negative or null underlying given");

            double strike = payoff.strike();

            if (payoff.optionType() == Option.Type.Put)
            {
                // use put-call simmetry
                Utils.swap <double>(ref spot, ref strike);
                Utils.swap <double>(ref riskFreeDiscount, ref dividendDiscount);
                payoff = new PlainVanillaPayoff(Option.Type.Call, strike);
            }

            if (dividendDiscount >= 1.0)
            {
                // early exercise is never optimal - use Black formula
                double          forwardPrice = spot * dividendDiscount / riskFreeDiscount;
                BlackCalculator black        = new BlackCalculator(payoff, forwardPrice, Math.Sqrt(variance), riskFreeDiscount);

                results_.value        = black.value();
                results_.delta        = black.delta(spot);
                results_.deltaForward = black.deltaForward();
                results_.elasticity   = black.elasticity(spot);
                results_.gamma        = black.gamma(spot);

                DayCounter rfdc  = process_.riskFreeRate().link.dayCounter();
                DayCounter divdc = process_.dividendYield().link.dayCounter();
                DayCounter voldc = process_.blackVolatility().link.dayCounter();
                double     t     = rfdc.yearFraction(process_.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate());
                results_.rho = black.rho(t);

                t = divdc.yearFraction(process_.dividendYield().link.referenceDate(), arguments_.exercise.lastDate());
                results_.dividendRho = black.dividendRho(t);

                t                    = voldc.yearFraction(process_.blackVolatility().link.referenceDate(), arguments_.exercise.lastDate());
                results_.vega        = black.vega(t);
                results_.theta       = black.theta(spot, t);
                results_.thetaPerDay = black.thetaPerDay(spot, t);

                results_.strikeSensitivity  = black.strikeSensitivity();
                results_.itmCashProbability = black.itmCashProbability();
            }
            else
            {
                // early exercise can be optimal - use approximation
                results_.value = americanCallApproximation(spot, strike, riskFreeDiscount, dividendDiscount, variance);
            }
        }