Exemple #1
0
 public GaussianKernel(double average, double sigma)
 {
    nd_ = new NormalDistribution(average,sigma); 
    cnd_ = new CumulativeNormalDistribution(average,sigma);
    // normFact is \sqrt{2*\pi}.
    normFact_ = Const.M_SQRT2*Const.M_SQRTPI;
 }
        public override void calculate()
        {
            double sigmaShift_vega  = 0.0001;
            double sigmaShift_volga = 0.0001;
            double spotShift_delta  = 0.0001 * spotFX_.link.value();
            double sigmaShift_vanna = 0.0001;

            Handle <Quote> x0Quote     = new Handle <Quote>(new SimpleQuote(spotFX_.link.value())); //used for shift
            Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value())); //used for shift

            BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.evaluationDate(),
                                                                    new NullCalendar(), atmVolQuote, new Actual365Fixed());
            BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_,
                                                                                   new Handle <BlackVolTermStructure>(blackVolTS));

            IPricingEngine engineBS = new AnalyticBarrierEngine(stochProcess);

            BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator(
                Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                atmVol_.link.value() * Math.Sqrt(T_));
            double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType());

            double call25Vol = vol25Call_.link.value();
            double put25Vol  = vol25Put_.link.value();

            BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator(
                Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                put25Vol * Math.Sqrt(T_));
            double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25);
            BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator(
                Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                call25Vol * Math.Sqrt(T_));
            double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25);

            //here use vanna volga interpolated smile to price vanilla
            List <double> strikes = new List <double>();
            List <double> vols    = new List <double>();

            strikes.Add(put25Strike);
            vols.Add(put25Vol);
            strikes.Add(atmStrike);
            vols.Add(atmVol_.link.value());
            strikes.Add(call25Strike);
            vols.Add(call25Vol);
            VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), domesticTS_.link.discount(T_),
                                                   foreignTS_.link.discount(T_), T_);
            Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols);

            interpolation.enableExtrapolation();
            StrikedTypePayoff payoff    = arguments_.payoff as StrikedTypePayoff;
            double            strikeVol = interpolation.value(payoff.strike());

            //vannila option price
            double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                                                      x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                      strikeVol * Math.Sqrt(T_),
                                                      domesticTS_.link.discount(T_));

            //spot > barrier up&out 0
            if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpOut)
            {
                results_.value = 0.0;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            //spot > barrier up&in vanilla
            else if (x0Quote.link.value() >= arguments_.barrier && arguments_.barrierType == Barrier.Type.UpIn)
            {
                results_.value = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            //spot < barrier down&out 0
            else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownOut)
            {
                results_.value = 0.0;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            //spot < barrier down&in vanilla
            else if (x0Quote.link.value() <= arguments_.barrier && arguments_.barrierType == Barrier.Type.DownIn)
            {
                results_.value = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            else
            {
                //set up BS barrier option pricing
                //only calculate out barrier option price
                // in barrier price = vanilla - out barrier
                Barrier.Type barrierTyp;
                if (arguments_.barrierType == Barrier.Type.UpOut)
                {
                    barrierTyp = arguments_.barrierType;
                }
                else if (arguments_.barrierType == Barrier.Type.UpIn)
                {
                    barrierTyp = Barrier.Type.UpOut;
                }
                else if (arguments_.barrierType == Barrier.Type.DownOut)
                {
                    barrierTyp = arguments_.barrierType;
                }
                else
                {
                    barrierTyp = Barrier.Type.DownOut;
                }

                BarrierOption barrierOption = new BarrierOption(barrierTyp,
                                                                arguments_.barrier.GetValueOrDefault(),
                                                                arguments_.rebate.GetValueOrDefault(),
                                                                (StrikedTypePayoff)arguments_.payoff,
                                                                arguments_.exercise);

                barrierOption.setPricingEngine(engineBS);

                //BS price with atm vol
                double priceBS = barrierOption.NPV();

                double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike,
                                                           x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                           atmVol_.link.value() * Math.Sqrt(T_),
                                                           domesticTS_.link.discount(T_));
                double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike,
                                                          x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                          atmVol_.link.value() * Math.Sqrt(T_),
                                                          domesticTS_.link.discount(T_));
                double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike,
                                                         x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                         atmVol_.link.value() * Math.Sqrt(T_),
                                                         domesticTS_.link.discount(T_));

                //market price
                double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike,
                                                            x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                            atmVol_.link.value() * Math.Sqrt(T_),
                                                            domesticTS_.link.discount(T_));

                double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike,
                                                           x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                           call25Vol * Math.Sqrt(T_),
                                                           domesticTS_.link.discount(T_));
                double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike,
                                                          x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                          put25Vol * Math.Sqrt(T_),
                                                          domesticTS_.link.discount(T_));


                //Analytical Black Scholes formula for vanilla option
                NormalDistribution norm  = new NormalDistribution();
                double             d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) /
                                                     domesticTS_.link.discount(T_) / atmStrike)
                                            + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) /
                                           (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vegaAtm_Analytical = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) *
                                            foreignTS_.link.discount(T_);
                double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() *
                                             (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) /
                                             atmVolQuote.link.value();

                double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) /
                                            domesticTS_.link.discount(T_) / call25Strike)
                                   + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vega25Call_Analytical = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) *
                                               foreignTS_.link.discount(T_);
                double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() *
                                                (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volga25Call_Analytical = vega25Call_Analytical * d125call *
                                                (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value();

                double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) /
                                           domesticTS_.link.discount(T_) / put25Strike)
                                  + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vega25Put_Analytical = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) *
                                              foreignTS_.link.discount(T_);
                double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() *
                                               (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volga25Put_Analytical = vega25Put_Analytical * d125Put *
                                               (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value();


                //BS vega
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega);
                barrierOption.recalculate();
                double vegaBarBS = (barrierOption.NPV() - priceBS) / sigmaShift_vega;

                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback

                //BS volga

                //vegaBar2
                //base NPV
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga);
                barrierOption.recalculate();
                double priceBS2 = barrierOption.NPV();

                //shifted npv
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega);
                barrierOption.recalculate();
                double vegaBarBS2 = (barrierOption.NPV() - priceBS2) / sigmaShift_vega;
                double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga;

                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value()
                                                                  - sigmaShift_volga
                                                                  - sigmaShift_vega);                     //setback

                //BS Delta
                //base delta
                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth
                barrierOption.recalculate();
                double priceBS_delta1 = barrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back
                barrierOption.recalculate();
                double priceBS_delta2 = barrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back
                double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);

                //shifted delta
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma
                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);          //shift forth
                barrierOption.recalculate();
                priceBS_delta1 = barrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back
                barrierOption.recalculate();
                priceBS_delta2 = barrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back
                double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);

                double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna;

                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back

                //Matrix
                Matrix A = new Matrix(3, 3, 0.0);

                //analytical
                A[0, 0] = vegaAtm_Analytical;
                A[0, 1] = vega25Call_Analytical;
                A[0, 2] = vega25Put_Analytical;
                A[1, 0] = vannaAtm_Analytical;
                A[1, 1] = vanna25Call_Analytical;
                A[1, 2] = vanna25Put_Analytical;
                A[2, 0] = volgaAtm_Analytical;
                A[2, 1] = volga25Call_Analytical;
                A[2, 2] = volga25Put_Analytical;

                Vector b = new Vector(3, 0.0);
                b[0] = vegaBarBS;
                b[1] = vannaBarBS;
                b[2] = volgaBarBS;

                //Vector q = inverse(A) * b; TODO implements transpose
                Vector q = Matrix.inverse(A) * b;


                //touch probability
                CumulativeNormalDistribution cnd = new CumulativeNormalDistribution();
                double mu = domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() -
                            foreignTS_.link.zeroRate(T_, Compounding.Continuous).value() -
                            Math.Pow(atmVol_.link.value(), 2.0) / 2.0;
                double h2 = (Math.Log(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value()) + mu * T_) /
                            (atmVol_.link.value() * Math.Sqrt(T_));
                double h2Prime = (Math.Log(x0Quote.link.value() / arguments_.barrier.GetValueOrDefault()) + mu * T_) /
                                 (atmVol_.link.value() * Math.Sqrt(T_));
                double probTouch = 0.0;
                if (arguments_.barrierType == Barrier.Type.UpIn || arguments_.barrierType == Barrier.Type.UpOut)
                {
                    probTouch = cnd.value(h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(),
                                                              2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(-h2);
                }
                else
                {
                    probTouch = cnd.value(-h2Prime) + Math.Pow(arguments_.barrier.GetValueOrDefault() / x0Quote.link.value(),
                                                               2.0 * mu / Math.Pow(atmVol_.link.value(), 2.0)) * cnd.value(h2);
                }
                double p_survival = 1.0 - probTouch;

                double lambda = p_survival;
                double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS)
                                + q[1] * (price25CallMkt - price25CallBS)
                                + q[2] * (price25PutMkt - price25PutBS);
                double outPrice = priceBS + lambda * adjust;//
                double inPrice;

                //adapt Vanilla delta
                if (adaptVanDelta_ == true)
                {
                    outPrice += lambda * (bsPriceWithSmile_ - vanillaOption);
                    //capfloored by (0, vanilla)
                    outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice));
                    inPrice  = bsPriceWithSmile_ - outPrice;
                }
                else
                {
                    //capfloored by (0, vanilla)
                    outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice));
                    inPrice  = vanillaOption - outPrice;
                }

                if (arguments_.barrierType == Barrier.Type.DownOut || arguments_.barrierType == Barrier.Type.UpOut)
                {
                    results_.value = outPrice;
                }
                else
                {
                    results_.value = inPrice;
                }

                results_.additionalResults["VanillaPrice"]    = vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = inPrice;
                results_.additionalResults["BarrierOutPrice"] = outPrice;
                results_.additionalResults["lambda"]          = lambda;
            }
        }
        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());
        }
Exemple #4
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"
        }
        public override void calculate()
        {
            // First: tests on types
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () =>
                             "not an European Option");

            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "not a plain-vanilla payoff");

            // forward values - futures, so b=0
            double forward1 = process1_.stateVariable().link.value();
            double forward2 = process2_.stateVariable().link.value();

            Date exerciseDate = arguments_.exercise.lastDate();

            // Volatilities
            double sigma1 = process1_.blackVolatility().link.blackVol(exerciseDate,
                                                                      forward1);
            double sigma2 = process2_.blackVolatility().link.blackVol(exerciseDate,
                                                                      forward2);

            double riskFreeDiscount = process1_.riskFreeRate().link.discount(exerciseDate);

            double strike = payoff.strike();

            // Unique F (forward) value for pricing
            double F = forward1 / (forward2 + strike);

            // Its volatility
            double sigma =
                Math.Sqrt(Math.Pow(sigma1, 2)
                          + Math.Pow((sigma2 * (forward2 / (forward2 + strike))), 2)
                          - 2 * rho_.link.value() * sigma1 * sigma2 * (forward2 / (forward2 + strike)));

            // Day counter and Dates handling variables
            DayCounter rfdc = process1_.riskFreeRate().link.dayCounter();
            double     t    = rfdc.yearFraction(process1_.riskFreeRate().link.referenceDate(),
                                                arguments_.exercise.lastDate());

            // Black-Scholes solution values
            double d1 = (Math.Log(F) + 0.5 * Math.Pow(sigma,
                                                      2) * t) / (sigma * Math.Sqrt(t));
            double d2 = d1 - sigma * Math.Sqrt(t);

            NormalDistribution           pdf = new NormalDistribution();
            CumulativeNormalDistribution cum = new CumulativeNormalDistribution();
            double Nd1  = cum.value(d1);
            double Nd2  = cum.value(d2);
            double NMd1 = cum.value(-d1);
            double NMd2 = cum.value(-d2);

            Option.Type optionType = payoff.optionType();

            if (optionType == Option.Type.Call)
            {
                results_.value = riskFreeDiscount * (F * Nd1 - Nd2) * (forward2 + strike);
            }
            else
            {
                results_.value = riskFreeDiscount * (NMd2 - F * NMd1) * (forward2 + strike);
            }

            double?callValue = optionType == Option.Type.Call ? results_.value :
                               riskFreeDiscount * (F * Nd1 - Nd2) * (forward2 + strike);

            results_.theta = (Math.Log(riskFreeDiscount) / t) * callValue +
                             riskFreeDiscount * (forward1 * sigma) / (2 * Math.Sqrt(t)) * pdf.value(d1);
        }
Exemple #6
0
        public override void calculate()
        {
            if (!(arguments_.exercise.type() == Exercise.Type.American))
                throw new ApplicationException("not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

            if (ex == null)
                throw new ApplicationException("non-American exercise given");

            if (ex.payoffAtExpiry())
                throw new ApplicationException("payoff at expiry not handled");

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;
            if (payoff == null)
                throw new ApplicationException("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();

            if (!(spot > 0.0))
                throw new ApplicationException("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;
                switch (payoff.optionType())
                {
                    case Option.Type.Call:
                        phi = 1;
                        break;
                    case Option.Type.Put:
                        phi = -1;
                        break;
                    default:
                        throw new ArgumentException("invalid option type");
                }
                //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"
        }
        public override void calculate()
        {
            double sigmaShift_vega  = 0.001;
            double sigmaShift_volga = 0.0001;
            double spotShift_delta  = 0.0001 * spotFX_.link.value();
            double sigmaShift_vanna = 0.0001;

            Utils.QL_REQUIRE(arguments_.barrierType == DoubleBarrier.Type.KnockIn ||
                             arguments_.barrierType == DoubleBarrier.Type.KnockOut, () =>
                             "Only same type barrier supported");

            Handle <Quote> x0Quote     = new Handle <Quote>(new SimpleQuote(spotFX_.link.value()));
            Handle <Quote> atmVolQuote = new Handle <Quote>(new SimpleQuote(atmVol_.link.value()));

            BlackVolTermStructure blackVolTS = new BlackConstantVol(Settings.evaluationDate(),
                                                                    new NullCalendar(), atmVolQuote, new Actual365Fixed());

            BlackScholesMertonProcess stochProcess = new BlackScholesMertonProcess(x0Quote, foreignTS_, domesticTS_,
                                                                                   new Handle <BlackVolTermStructure>(blackVolTS));

            IPricingEngine engineBS = getOriginalEngine_(stochProcess, series_);

            BlackDeltaCalculator blackDeltaCalculatorAtm = new BlackDeltaCalculator(
                Option.Type.Call, atmVol_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                atmVol_.link.value() * Math.Sqrt(T_));

            double atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_.link.atmType());

            double call25Vol = vol25Call_.link.value();
            double put25Vol  = vol25Put_.link.value();
            BlackDeltaCalculator blackDeltaCalculatorPut25 = new BlackDeltaCalculator(
                Option.Type.Put, vol25Put_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                put25Vol * Math.Sqrt(T_));
            double put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25);
            BlackDeltaCalculator blackDeltaCalculatorCall25 = new BlackDeltaCalculator(
                Option.Type.Call, vol25Call_.link.deltaType(), x0Quote.link.value(),
                domesticTS_.link.discount(T_), foreignTS_.link.discount(T_),
                call25Vol * Math.Sqrt(T_));
            double call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25);

            //here use vanna volga interpolated smile to price vanilla
            List <double> strikes = new List <double>();
            List <double> vols    = new List <double>();

            strikes.Add(put25Strike);
            vols.Add(put25Vol);
            strikes.Add(atmStrike);
            vols.Add(atmVol_.link.value());
            strikes.Add(call25Strike);
            vols.Add(call25Vol);
            VannaVolga vannaVolga = new VannaVolga(x0Quote.link.value(), foreignTS_.link.discount(T_),
                                                   foreignTS_.link.discount(T_), T_);
            Interpolation interpolation = vannaVolga.interpolate(strikes, strikes.Count, vols);

            interpolation.enableExtrapolation();
            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            Utils.QL_REQUIRE(payoff != null, () => "invalid payoff");
            double strikeVol = interpolation.value(payoff.strike());
            //vannila option price
            double vanillaOption = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                                                      x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                      strikeVol * Math.Sqrt(T_),
                                                      domesticTS_.link.discount(T_));

            //already out
            if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) &&
                arguments_.barrierType == DoubleBarrier.Type.KnockOut)
            {
                results_.value = 0.0;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            //already in
            else if ((x0Quote.link.value() > arguments_.barrier_hi || x0Quote.link.value() < arguments_.barrier_lo) &&
                     arguments_.barrierType == DoubleBarrier.Type.KnockIn)
            {
                results_.value = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["VanillaPrice"]    = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
                results_.additionalResults["BarrierOutPrice"] = 0.0;
            }
            else
            {
                //set up BS barrier option pricing
                //only calculate out barrier option price
                // in barrier price = vanilla - out barrier
                //StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;
                DoubleBarrierOption doubleBarrierOption = new DoubleBarrierOption(
                    arguments_.barrierType,
                    arguments_.barrier_lo.GetValueOrDefault(),
                    arguments_.barrier_hi.GetValueOrDefault(),
                    arguments_.rebate.GetValueOrDefault(),
                    payoff,
                    arguments_.exercise);

                doubleBarrierOption.setPricingEngine(engineBS);

                //BS price
                double priceBS = doubleBarrierOption.NPV();

                double priceAtmCallBS = Utils.blackFormula(Option.Type.Call, atmStrike,
                                                           x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                           atmVol_.link.value() * Math.Sqrt(T_),
                                                           domesticTS_.link.discount(T_));
                double price25CallBS = Utils.blackFormula(Option.Type.Call, call25Strike,
                                                          x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                          atmVol_.link.value() * Math.Sqrt(T_),
                                                          domesticTS_.link.discount(T_));
                double price25PutBS = Utils.blackFormula(Option.Type.Put, put25Strike,
                                                         x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                         atmVol_.link.value() * Math.Sqrt(T_),
                                                         domesticTS_.link.discount(T_));

                //market price
                double priceAtmCallMkt = Utils.blackFormula(Option.Type.Call, atmStrike,
                                                            x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                            atmVol_.link.value() * Math.Sqrt(T_),
                                                            domesticTS_.link.discount(T_));
                double price25CallMkt = Utils.blackFormula(Option.Type.Call, call25Strike,
                                                           x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                           call25Vol * Math.Sqrt(T_),
                                                           domesticTS_.link.discount(T_));
                double price25PutMkt = Utils.blackFormula(Option.Type.Put, put25Strike,
                                                          x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_),
                                                          put25Vol * Math.Sqrt(T_),
                                                          domesticTS_.link.discount(T_));

                //Analytical Black Scholes formula
                NormalDistribution norm  = new NormalDistribution();
                double             d1atm = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / atmStrike)
                                            + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vegaAtm_Analytical  = x0Quote.link.value() * norm.value(d1atm) * Math.Sqrt(T_) * foreignTS_.link.discount(T_);
                double vannaAtm_Analytical = vegaAtm_Analytical / x0Quote.link.value() * (1.0 - d1atm / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value();

                double d125call = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / call25Strike)
                                   + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vega25Call_Analytical  = x0Quote.link.value() * norm.value(d125call) * Math.Sqrt(T_) * foreignTS_.link.discount(T_);
                double vanna25Call_Analytical = vega25Call_Analytical / x0Quote.link.value() * (1.0 - d125call / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value();

                double d125Put = (Math.Log(x0Quote.link.value() * foreignTS_.link.discount(T_) / domesticTS_.link.discount(T_) / put25Strike)
                                  + 0.5 * Math.Pow(atmVolQuote.link.value(), 2.0) * T_) / (atmVolQuote.link.value() * Math.Sqrt(T_));
                double vega25Put_Analytical  = x0Quote.link.value() * norm.value(d125Put) * Math.Sqrt(T_) * foreignTS_.link.discount(T_);
                double vanna25Put_Analytical = vega25Put_Analytical / x0Quote.link.value() * (1.0 - d125Put / (atmVolQuote.link.value() * Math.Sqrt(T_)));
                double volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote.link.value() * Math.Sqrt(T_)) / atmVolQuote.link.value();


                //BS vega
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega);
                doubleBarrierOption.recalculate();
                double vegaBarBS = (doubleBarrierOption.NPV() - priceBS) / sigmaShift_vega;
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vega);//setback

                //BS volga

                //vegaBar2
                //base NPV
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_volga);
                doubleBarrierOption.recalculate();
                double priceBS2 = doubleBarrierOption.NPV();

                //shifted npv
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vega);
                doubleBarrierOption.recalculate();
                double vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2) / sigmaShift_vega;
                double volgaBarBS = (vegaBarBS2 - vegaBarBS) / sigmaShift_volga;
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value()
                                                                  - sigmaShift_volga
                                                                  - sigmaShift_vega);                        //setback

                //BS Delta
                //base delta
                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//shift forth
                doubleBarrierOption.recalculate();
                double priceBS_delta1 = doubleBarrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back
                doubleBarrierOption.recalculate();
                double priceBS_delta2 = doubleBarrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back
                double deltaBar1 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);

                //shifted vanna
                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() + sigmaShift_vanna); //shift sigma
                //shifted delta
                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);          //shift forth
                doubleBarrierOption.recalculate();
                priceBS_delta1 = doubleBarrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() - 2 * spotShift_delta);//shift back
                doubleBarrierOption.recalculate();
                priceBS_delta2 = doubleBarrierOption.NPV();

                ((SimpleQuote)x0Quote.currentLink()).setValue(x0Quote.link.value() + spotShift_delta);//set back
                double deltaBar2 = (priceBS_delta1 - priceBS_delta2) / (2.0 * spotShift_delta);

                double vannaBarBS = (deltaBar2 - deltaBar1) / sigmaShift_vanna;

                ((SimpleQuote)atmVolQuote.currentLink()).setValue(atmVolQuote.link.value() - sigmaShift_vanna);//set back

                //Matrix
                Matrix A = new Matrix(3, 3, 0.0);

                //analytical
                A[0, 0] = vegaAtm_Analytical;
                A[0, 1] = vega25Call_Analytical;
                A[0, 2] = vega25Put_Analytical;
                A[1, 0] = vannaAtm_Analytical;
                A[1, 1] = vanna25Call_Analytical;
                A[1, 2] = vanna25Put_Analytical;
                A[2, 0] = volgaAtm_Analytical;
                A[2, 1] = volga25Call_Analytical;
                A[2, 2] = volga25Put_Analytical;

                Vector b = new Vector(3, 0.0);
                b[0] = vegaBarBS;
                b[1] = vannaBarBS;
                b[2] = volgaBarBS;
                Vector q = Matrix.inverse(A) * b;

                double H = arguments_.barrier_hi.GetValueOrDefault();
                double L = arguments_.barrier_lo.GetValueOrDefault();
                double theta_tilt_minus = ((domesticTS_.link.zeroRate(T_, Compounding.Continuous).value() -
                                            foreignTS_.link.zeroRate(T_, Compounding.Continuous).value()) /
                                           atmVol_.link.value() - atmVol_.link.value() / 2.0) * Math.Sqrt(T_);
                double h = 1.0 / atmVol_.link.value() * Math.Log(H / x0Quote.link.value()) / Math.Sqrt(T_);
                double l = 1.0 / atmVol_.link.value() * Math.Log(L / x0Quote.link.value()) / Math.Sqrt(T_);
                CumulativeNormalDistribution cnd = new CumulativeNormalDistribution();

                double doubleNoTouch = 0.0;
                for (int j = -series_; j < series_; j++)
                {
                    double e_minus = 2 * j * (h - l) - theta_tilt_minus;
                    doubleNoTouch += Math.Exp(-2.0 * j * theta_tilt_minus * (h - l)) * (cnd.value(h + e_minus) - cnd.value(l + e_minus))
                                     - Math.Exp(-2.0 * j * theta_tilt_minus * (h - l) + 2.0 * theta_tilt_minus * h) *
                                     (cnd.value(h - 2.0 * h + e_minus) - cnd.value(l - 2.0 * h + e_minus));
                }

                double p_survival = doubleNoTouch;

                double lambda = p_survival;
                double adjust = q[0] * (priceAtmCallMkt - priceAtmCallBS)
                                + q[1] * (price25CallMkt - price25CallBS)
                                + q[2] * (price25PutMkt - price25PutBS);
                double outPrice = priceBS + lambda * adjust;//
                double inPrice;

                //adapt Vanilla delta
                if (adaptVanDelta_ == true)
                {
                    outPrice += lambda * (bsPriceWithSmile_ - vanillaOption);
                    //capfloored by (0, vanilla)
                    outPrice = Math.Max(0.0, Math.Min(bsPriceWithSmile_, outPrice));
                    inPrice  = bsPriceWithSmile_ - outPrice;
                }
                else
                {
                    //capfloored by (0, vanilla)
                    outPrice = Math.Max(0.0, Math.Min(vanillaOption, outPrice));
                    inPrice  = vanillaOption - outPrice;
                }

                if (arguments_.barrierType == DoubleBarrier.Type.KnockOut)
                {
                    results_.value = outPrice;
                }
                else
                {
                    results_.value = inPrice;
                }

                results_.additionalResults["VanillaPrice"]    = vanillaOption;
                results_.additionalResults["BarrierInPrice"]  = inPrice;
                results_.additionalResults["BarrierOutPrice"] = outPrice;
                results_.additionalResults["lambda"]          = lambda;
            }
        }
Exemple #8
0
        public void testOperatorConsistency()
        {
            //("Testing differential operators...");

             NormalDistribution normal = new NormalDistribution(average, sigma);
             CumulativeNormalDistribution cum = new CumulativeNormalDistribution(average, sigma);

             double xMin = average - 4 * sigma,
              xMax = average + 4 * sigma;
             int N = 10001;
             double h = (xMax - xMin) / (N - 1);

             Vector x = new Vector(N),
             y = new Vector(N),
             yi = new Vector(N),
             yd = new Vector(N),
             temp = new Vector(N),
             diff = new Vector(N);

             for (int i = 0; i < N; i++)
            x[i] = xMin + h * i;

             for (int i = 0; i < x.Count; i++)
            y[i] = normal.value(x[i]);
             for (int i = 0; i < x.Count; i++)
            yi[i] = cum.value(x[i]);

             for (int i = 0; i < x.size(); i++)
            yd[i] = normal.derivative(x[i]);

             // define the differential operators
             DZero D = new DZero(N, h);
             DPlusDMinus D2 = new DPlusDMinus(N, h);

             // check that the derivative of cum is Gaussian
             temp = D.applyTo(yi);

             for (int i = 0; i < y.Count; i++)
            diff[i] = y[i] - temp[i];
             double e = Utilities.norm(diff, diff.size(), h);
             if (e > 1.0e-6)
             {
            Assert.Fail("norm of 1st derivative of cum minus Gaussian: " + e + "\ntolerance exceeded");
             }

             // check that the second derivative of cum is normal.derivative
             temp = D2.applyTo(yi);

             for (int i = 0; i < yd.Count; i++)
            diff[i] = yd[i] - temp[i];

             e = Utilities.norm(diff, diff.size(), h);
             if (e > 1.0e-4)
             {
            Assert.Fail("norm of 2nd derivative of cum minus Gaussian derivative: " + e + "\ntolerance exceeded");
             }
        }
Exemple #9
0
        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");
            */

            if(!(arguments_.exercise.type() == Exercise.Type.European))
                throw new ApplicationException("not an European Option");

            double runningLog;
            int pastFixings;
            if (arguments_.averageType == Average.Type.Geometric) {
                if(!(arguments_.runningAccumulator>0.0))
                    throw new ApplicationException("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 = (PlainVanillaPayoff)(arguments_.payoff);
            if (payoff == null)
                throw new ApplicationException("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 InitializedList<double>(arguments_.fixingDates.Count());
            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 = std::accumulate(fixingTimes.begin(),
                                           fixingTimes.end(), 0.0);*/
            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();
            if(!(s > 0.0))
                throw new ApplicationException("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());
        }