Beispiel #1
0
        public static double bachelierBlackFormula( Option.Type optionType,
            double strike,
            double forward,
            double stdDev,
            double discount)
        {
            if ( stdDev < 0.0 )
                throw new ArgumentException( "stdDev (" + stdDev + ") must be non-negative" );

            if ( discount <= 0.0 )
                throw new ArgumentException( "discount (" + discount + ") must be positive" );

            double d = ( forward - strike ) * (int)optionType;
            double h = d / stdDev;

            if ( stdDev == 0.0 )
                return discount * Math.Max( d, 0.0 );

            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();

            double result = discount * ( stdDev * phi.derivative( h ) + d * phi.value( h ) );

            if ( !( result >= 0.0 ) )
                throw new ApplicationException( "negative value (" + result + ") for " +
                        stdDev + " stdDev, " +
                        optionType + " option, " +
                        strike + " strike , " +
                        forward + " forward" );

            return result;
        }
Beispiel #2
0
        public static double blackFormulaStdDevDerivative(double strike,
                                                          double forward,
                                                          double stdDev,
                                                          double discount,
                                                          double displacement)
        {
            checkParameters(strike, forward, displacement);

            if (stdDev < 0.0)
            {
                throw new ArgumentException("stdDev (" + stdDev + ") must be non-negative");
            }

            if (discount <= 0.0)
            {
                throw new ArgumentException("discount (" + discount + ") must be positive");
            }

            forward = forward + displacement;
            strike  = strike + displacement;

            if (stdDev == 0.0 || strike == 0)
            {
                return(0.0);
            }

            double d1 = Math.Log(forward / strike) / stdDev + .5 * stdDev;
            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();

            return(discount * forward * phi.derivative(d1));
        }
Beispiel #3
0
		public static double blackFormula( Option.Type optionType, double strike, double forward, double stdDev,
													 double discount, double displacement )
		{
			checkParameters( strike, forward, displacement );
			if ( !( stdDev >= 0.0 ) )
				throw new ApplicationException( "stdDev (" + stdDev + ") must be non-negative" );
			if ( !( discount > 0.0 ) )
				throw new ApplicationException( "discount (" + discount + ") must be positive" );

			if ( stdDev == 0.0 )
				return Math.Max( ( forward - strike ) * (int)optionType, 0.0 ) * discount;

			forward = forward + displacement;
			strike = strike + displacement;

			// since displacement is non-negative strike==0 iff displacement==0
			// so returning forward*discount is OK 
			if ( strike == 0.0 )
				return ( optionType == Option.Type.Call ? forward * discount : 0.0 );

			double d1 = Math.Log( forward / strike ) / stdDev + 0.5 * stdDev;
			double d2 = d1 - stdDev;
			CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
			double nd1 = phi.value( (int)optionType * d1 );
			double nd2 = phi.value( (int)optionType * d2 );
			double result = discount * (int)optionType * ( forward * nd1 - strike * nd2 );
			if ( !( result >= 0.0 ) )
				throw new ApplicationException( "negative value (" + result + ") for " +
						stdDev + " stdDev, " +
						optionType + " option, " +
						strike + " strike , " +
						forward + " forward" );
			return result;
		}
Beispiel #4
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;
 }
Beispiel #5
0
        protected double digitalPriceWithoutSmile(double strike, double initialValue, double expiry, double deflator)
        {
            double lambdaS = smilesOnExpiry_.volatility(strike);
            double lambdaT = smilesOnPayment_.volatility(strike);

            List <double> lambdaU  = lambdasOverPeriod(expiry, lambdaS, lambdaT);
            double        variance = startTime_ * lambdaU[0] * lambdaU[0] + (expiry - startTime_) * lambdaU[1] * lambdaU[1];

            double lambdaSATM = smilesOnExpiry_.volatility(initialValue);
            double lambdaTATM = smilesOnPayment_.volatility(initialValue);
            //drift of Lognormal process (of Libor) "a_U()" nel paper
            List <double> muU        = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);
            double        adjustment = (startTime_ * muU[0] + (expiry - startTime_) * muU[1]);


            double d2 = (Math.Log(initialValue / strike) + adjustment - 0.5 * variance) / Math.Sqrt(variance);

            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            double result = deflator * phi.value(d2);

            Utils.QL_REQUIRE(result > 0.0, () =>
                             "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result< 0. Result:" + result);
            Utils.QL_REQUIRE(result / deflator <= 1.0, () =>
                             "RangeAccrualPricerByBgm::digitalPriceWithoutSmile: result/deflator > 1. Ratio: "
                             + result / deflator + " result: " + result + " deflator: " + deflator);

            return(result);
        }
Beispiel #6
0
        public static double bachelierBlackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount)
        {
            if (stdDev < 0.0)
            {
                throw new ArgumentException("stdDev (" + stdDev + ") must be non-negative");
            }

            if (discount <= 0.0)
            {
                throw new ArgumentException("discount (" + discount + ") must be positive");
            }

            double d = (forward - strike) * (int)optionType;
            double h = d / stdDev;

            if (stdDev == 0.0)
            {
                return(discount * Math.Max(d, 0.0));
            }

            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            double result = discount * (stdDev * phi.derivative(h) + d * phi.value(h));

            if (!(result >= 0.0))
            {
                throw new ApplicationException("negative value (" + result + ") for " +
                                               stdDev + " stdDev, " +
                                               optionType + " option, " +
                                               strike + " strike , " +
                                               forward + " forward");
            }

            return(result);
        }
Beispiel #7
0
        /*! Black style formula when forward is normal rather than
         * log-normal. This is essentially the model of Bachelier.
         *
         *  \warning Bachelier model needs absolute volatility, not
         *     percentage volatility. Standard deviation is
         *     absoluteVolatility*sqrt(timeToMaturity)
         */
        public static double bachelierBlackFormula(Option.Type optionType,
                                                   double strike,
                                                   double forward,
                                                   double stdDev,
                                                   double discount = 1.0)
        {
            QL_REQUIRE(stdDev >= 0.0, () =>
                       "stdDev (" + stdDev + ") must be non-negative");
            QL_REQUIRE(discount > 0.0, () =>
                       "discount (" + discount + ") must be positive");
            double d = (forward - strike) * (int)optionType, h = d / stdDev;

            if (stdDev.IsEqual(0.0))
            {
                return(discount * Math.Max(d, 0.0));
            }
            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            double result = discount * (stdDev * phi.derivative(h) + d * phi.value(h));

            QL_REQUIRE(result >= 0.0, () =>
                       "negative value (" + result + ") for " +
                       stdDev + " stdDev, " +
                       optionType + " option, " +
                       strike + " strike , " +
                       forward + " forward");
            return(result);
        }
Beispiel #8
0
        public freeArbSVI(List <double> strikes,
                          List <double> times,
                          double spot,
                          Handle <YieldTermStructure> riskFreeTS,
                          Handle <YieldTermStructure> dividendTS,
                          Matrix crudeVolSurface,
                          double lambda)
        {
            // sanity check to add : check date + strikes vs vol surface

            strikes_    = strikes;
            times_      = times;
            spot_       = spot;
            riskFreeTS_ = riskFreeTS;
            dividendTS_ = dividendTS;

            fwMoneynessGrid_ = new Matrix(times_.Count, strikes_.Count, 0.0);
            totalVariance_   = new Matrix(times_.Count, strikes_.Count, 0.0);
            X_ = new Matrix(times_.Count, 2 * strikes_.Count + 2, 0.0);
            crudeVolSurface_ = new Matrix(crudeVolSurface);
            cumulNormdiv_    = new CumulativeNormalDistribution();

            lambda_        = lambda;
            g_i_           = new List <CubicInterpolation>();
            A_             = new Matrix(2 * strikes_.Count + 2, strikes_.Count, 0.0);;
            B_             = new Matrix(2 * strikes_.Count + 2, 2 * strikes_.Count + 1, 0.0);;
            strikesSpline_ = new InitializedList <List <double> >();
        }
Beispiel #9
0
        public AnalyticDoubleBarrierEngine(GeneralizedBlackScholesProcess process, int series = 5)
        {
            process_ = process;
            series_  = series;
            f_       = new CumulativeNormalDistribution();

            process_.registerWith(update);
        }
Beispiel #10
0
        //! gaussian-assumption Average Shortfall (averaged shortfallness)
        public double gaussianAverageShortfall(double target)
        {
            double m   = this.mean();
            double std = this.standardDeviation();
            CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(m, std);
            NormalDistribution           g         = new NormalDistribution(m, std);

            return((target - m) + std * std * g.value(target) / gIntegral.value(target));
        }
Beispiel #11
0
        protected double smileCorrection(double strike,
                                         double forward,
                                         double expiry,
                                         double deflator)
        {
            double previousStrike = strike - eps_ / 2;
            double nextStrike     = strike + eps_ / 2;

            double derSmileS = (smilesOnExpiry_.volatility(nextStrike) -
                                smilesOnExpiry_.volatility(previousStrike)) / eps_;
            double derSmileT = (smilesOnPayment_.volatility(nextStrike) -
                                smilesOnPayment_.volatility(previousStrike)) / eps_;

            double lambdaS = smilesOnExpiry_.volatility(strike);
            double lambdaT = smilesOnPayment_.volatility(strike);

            double derLambdaDerK = derLambdaDerLambdaS(expiry) * derSmileS +
                                   derLambdaDerLambdaT(expiry) * derSmileT;


            double        lambdaSATM         = smilesOnExpiry_.volatility(forward);
            double        lambdaTATM         = smilesOnPayment_.volatility(forward);
            List <double> lambdasOverPeriodU = lambdasOverPeriod(expiry, lambdaS, lambdaT);
            //drift of Lognormal process (of Libor) "a_U()" nel paper
            List <double> muU = driftsOverPeriod(expiry, lambdaSATM, lambdaTATM, correlation_);

            double variance = Math.Max(startTime_, 0.0) * lambdasOverPeriodU[0] * lambdasOverPeriodU[0] +
                              Math.Min(expiry - startTime_, expiry) * lambdasOverPeriodU[1] * lambdasOverPeriodU[1];

            double forwardAdjustment = Math.Exp(Math.Max(startTime_, 0.0) * muU[0] +
                                                Math.Min(expiry - startTime_, expiry) * muU[1]);
            double forwardAdjusted = forward * forwardAdjustment;

            double d1 = (Math.Log(forwardAdjusted / strike) + 0.5 * variance) / Math.Sqrt(variance);

            double sqrtOfTimeToExpiry = (Math.Max(startTime_, 0.0) * lambdasOverPeriodU[0] +
                                         Math.Min(expiry - startTime_, expiry) * lambdasOverPeriodU[1]) * (1.0 / Math.Sqrt(variance));

            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            NormalDistribution           psi = new NormalDistribution();
            double result = -forwardAdjusted *psi.value(d1) * sqrtOfTimeToExpiry * derLambdaDerK;

            result *= deflator;

            Utils.QL_REQUIRE(Math.Abs(result / deflator) <= 1.0 + Math.Pow(eps_, .2), () =>
                             "RangeAccrualPricerByBgm::smileCorrection: abs(result/deflator) > 1. Ratio: "
                             + result / deflator + " result: " + result + " deflator: " + deflator);

            return(result);
        }
Beispiel #12
0
        /*! returns the variance of observations below target
         *  \f[ \frac{\sum w_i (min(0, x_i-target))^2 }{\sum w_i}. \f]
         *
         *  See Dembo, Freeman "The Rules Of Risk", Wiley (2001)
         */
        public double gaussianRegret(double target)
        {
            double m        = this.mean();
            double std      = this.standardDeviation();
            double variance = std * std;
            CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(m, std);
            NormalDistribution           g         = new NormalDistribution(m, std);
            double firstTerm  = variance + m * m - 2.0 * target * m + target * target;
            double alfa       = gIntegral.value(target);
            double secondTerm = m - target;
            double beta       = variance * g.value(target);
            double result     = alfa * firstTerm - beta * secondTerm;

            return(result / alfa);
        }
Beispiel #13
0
        public static double blackFormula(Option.Type optionType, double strike, double forward, double stdDev, double discount, double displacement)
        {
            checkParameters(strike, forward, displacement);

            if (!(stdDev >= 0.0))
            {
                throw new ApplicationException("stdDev (" + stdDev + ") must be non-negative");
            }

            if (!(discount > 0.0))
            {
                throw new ApplicationException("discount (" + discount + ") must be positive");
            }

            if (stdDev == 0.0)
            {
                return(Math.Max((forward - strike) * (int)optionType, 0.0) * discount);
            }

            forward = forward + displacement;
            strike  = strike + displacement;

            // since displacement is non-negative strike==0 iff displacement==0
            // so returning forward*discount is OK
            if (strike == 0.0)
            {
                return(optionType == Option.Type.Call ? forward * discount : 0.0);
            }

            double d1 = Math.Log(forward / strike) / stdDev + 0.5 * stdDev;
            double d2 = d1 - stdDev;
            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            double nd1    = phi.value((int)optionType * d1);
            double nd2    = phi.value((int)optionType * d2);
            double result = discount * (int)optionType * (forward * nd1 - strike * nd2);

            if (!(result >= 0.0))
            {
                throw new ApplicationException("negative value (" + result + ") for " +
                                               stdDev + " stdDev, " +
                                               optionType + " option, " +
                                               strike + " strike , " +
                                               forward + " forward");
            }

            return(result);
        }
Beispiel #14
0
        public double nD2(double strike)     // n(d2)
        {
            double d2_   = 0.0;
            double n_d2_ = 0.0; // n(d2)

            if (stdDev_ >= Const.QL_EPSILON)
            {
                if (strike > 0)
                {
                    d2_ = Math.Log(forward_ / strike) / stdDev_ - 0.5 * stdDev_;
                    CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                    n_d2_ = f.derivative(d2_);
                }
            }

            return(n_d2_);
        }
Beispiel #15
0
 public BlackImpliedStdDevHelper(Option.Type optionType,
                                 double strike,
                                 double forward,
                                 double undiscountedBlackPrice,
                                 double displacement = 0.0)
 {
     halfOptionType_         = 0.5 * (int)optionType;
     signedStrike_           = (int)optionType * (strike + displacement);
     signedForward_          = (int)optionType * (forward + displacement);
     undiscountedBlackPrice_ = undiscountedBlackPrice;
     N_ = new CumulativeNormalDistribution();
     checkParameters(strike, forward, displacement);
     Utils.QL_REQUIRE(undiscountedBlackPrice >= 0.0, () =>
                      "undiscounted Black price (" +
                      undiscountedBlackPrice + ") must be non-negative");
     signedMoneyness_ = (int)optionType * Math.Log((forward + displacement) / (strike + displacement));
 }
Beispiel #16
0
            public double value(double x)
            {
                CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
                double temp = (x - mux_) / sigmax_;
                double txy  = Math.Sqrt(1.0 - rhoxy_ * rhoxy_);

                Vector lambda = new Vector(size_);
                int    i;

                for (i = 0; i < size_; i++)
                {
                    double tau = (i == 0 ? t_[0] - T_ : t_[i] - t_[i - 1]);
                    double c   = (i == size_ - 1 ? (1.0 + rate_ * tau) : rate_ * tau);
                    lambda[i] = c * A_[i] * Math.Exp(-Ba_[i] * x);
                }

                //SolvingFunction function = new SolvingFunction(lambda, Bb_);
                //verifier le polymorphisme
                SolvingFunction function = new SolvingFunction(lambda, Bb_);
                Brent           s1d      = new Brent();

                s1d.setMaxEvaluations(1000);
                double yb = s1d.solve(function, 1e-6, 0.00, -100.0, 100.0);

                double h1 = (yb - muy_) / (sigmay_ * txy) -
                            rhoxy_ * (x - mux_) / (sigmax_ * txy);
                double value = phi.value(-w_ * h1);


                for (i = 0; i < size_; i++)
                {
                    double h2 = h1 +
                                Bb_[i] * sigmay_ *Math.Sqrt(1.0 - rhoxy_ *rhoxy_);

                    double kappa = -Bb_[i] *
                                   (muy_ - 0.5 * txy * txy * sigmay_ * sigmay_ * Bb_[i] +
                                    rhoxy_ * sigmay_ * (x - mux_) / sigmax_);
                    value -= lambda[i] * Math.Exp(kappa) * phi.value(-w_ * h2);
                }

                return(Math.Exp(-0.5 * temp * temp) * value /
                       (sigmax_ * Math.Sqrt(2.0 * QLNet.Const.M_PI)));
            }
Beispiel #17
0
        public double cumD2(double strike)   // N(d2) or N(-d2)
        {
            double d2_         = 0.0;
            double cum_d2_pos_ = 1.0; // N(d2)
            double cum_d2_neg_ = 0.0; // N(-d2)

            CumulativeNormalDistribution f = new CumulativeNormalDistribution();

            if (stdDev_ >= Const.QL_EPSILON)
            {
                if (strike > 0)
                {
                    d2_ = Math.Log(forward_ / strike) / stdDev_ - 0.5 * stdDev_;
                    return(f.value(phi_ * d2_));
                }
            }
            else
            {
                if (forward_ < strike)
                {
                    cum_d2_pos_ = 0.0;
                    cum_d2_neg_ = 1.0;
                }
                else if (forward_ == strike)
                {
                    d2_ = -0.5 * stdDev_;
                    return(f.value(phi_ * d2_));
                }
            }

            if (phi_ > 0)
            {
                // if Call
                return(cum_d2_pos_);
            }
            else
            {
                return(cum_d2_neg_);
            }
        }
Beispiel #18
0
        public double cumD1(double strike)   // N(d1) or N(-d1)
        {
            double d1_         = 0.0;
            double cum_d1_pos_ = 1.0; // N(d1)
            double cum_d1_neg_ = 0.0; // N(-d1)

            CumulativeNormalDistribution f = new CumulativeNormalDistribution();

            if (stdDev_ >= Const.QL_EPSILON)
            {
                if (strike > 0)
                {
                    d1_ = Math.Log(forward_ / strike) / stdDev_ + 0.5 * stdDev_;
                    return(f.value(phi_ * d1_));
                }
            }
            else
            {
                if (forward_ < strike)
                {
                    cum_d1_pos_ = 0.0;
                    cum_d1_neg_ = 1.0;
                }
                else if (forward_.IsEqual(strike))
                {
                    d1_ = 0.5 * stdDev_;
                    return(f.value(phi_ * d1_));
                }
            }

            if (phi_ > 0)
            {
                // if Call
                return(cum_d1_pos_);
            }
            else
            {
                return(cum_d1_neg_);
            }
        }
Beispiel #19
0
        // calculate the value of euro min basket call
        private double euroTwoAssetMinBasketCall(double forward1, double forward2, double strike, double riskFreeDiscount,
                                                 double variance1, double variance2, double rho)
        {
            double stdDev1 = Math.Sqrt(variance1);
            double stdDev2 = Math.Sqrt(variance2);

            double variance = variance1 + variance2 - 2 * rho * stdDev1 * stdDev2;
            double stdDev   = Math.Sqrt(variance);

            double modRho1 = (rho * stdDev2 - stdDev1) / stdDev;
            double modRho2 = (rho * stdDev1 - stdDev2) / stdDev;

            double D1 = (Math.Log(forward1 / forward2) + 0.5 * variance) / stdDev;

            double alfa, beta, gamma;

            if (strike != 0.0)
            {
                BivariateCumulativeNormalDistribution bivCNorm     = new BivariateCumulativeNormalDistribution(rho);
                BivariateCumulativeNormalDistribution bivCNormMod2 = new BivariateCumulativeNormalDistribution(modRho2);
                BivariateCumulativeNormalDistribution bivCNormMod1 = new BivariateCumulativeNormalDistribution(modRho1);

                double D1_1 = (Math.Log(forward1 / strike) + 0.5 * variance1) / stdDev1;
                double D1_2 = (Math.Log(forward2 / strike) + 0.5 * variance2) / stdDev2;
                alfa  = bivCNormMod1.value(D1_1, -D1);
                beta  = bivCNormMod2.value(D1_2, D1 - stdDev);
                gamma = bivCNorm.value(D1_1 - stdDev1, D1_2 - stdDev2);
            }
            else
            {
                CumulativeNormalDistribution cum = new CumulativeNormalDistribution();
                alfa  = cum.value(-D1);
                beta  = cum.value(D1 - stdDev);
                gamma = 1.0;
            }

            return(riskFreeDiscount * (forward1 * alfa + forward2 * beta - strike * gamma));
        }
Beispiel #20
0
        /*! Black 1976 probability of being in the money (in the bond martingale measure), i.e. N(d2).
         *    It is a risk-neutral probability, not the real world one.
         *     \warning instead of volatility it uses standard deviation, i.e. volatility*sqrt(timeToMaturity)
         */
        public static double blackFormulaCashItmProbability(Option.Type optionType,
                                                            double strike,
                                                            double forward,
                                                            double stdDev,
                                                            double displacement = 0.0)
        {
            checkParameters(strike, forward, displacement);
            if (stdDev.IsEqual(0.0))
            {
                return(forward * (int)optionType > strike * (int)optionType ? 1.0 : 0.0);
            }

            forward = forward + displacement;
            strike  = strike + displacement;
            if (strike.IsEqual(0.0))
            {
                return(optionType == Option.Type.Call ? 1.0 : 0.0);
            }
            double d2 = Math.Log(forward / strike) / stdDev - 0.5 * stdDev;
            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();

            return(phi.value((int)optionType * d2));
        }
Beispiel #21
0
        //Hagan, 3.5b, 3.5c
        protected override double optionletPrice(Option.Type optionType, double strike)
        {
            double variance = swaptionVolatility().link.blackVariance(fixingDate_, swapTenor_, swapRateValue_);
            double firstDerivativeOfGAtForwardValue = gFunction_.firstDerivative(swapRateValue_);
            double price = 0;

            double CK = vanillaOptionPricer_.value(strike, optionType, annuity_);
            price += (discount_ / annuity_) * CK;
            double sqrtSigma2T = Math.Sqrt(variance);
            double lnRoverK = Math.Log(swapRateValue_ / strike);
            double d32 = (lnRoverK + 1.5 * variance) / sqrtSigma2T;
            double d12 = (lnRoverK + .5 * variance) / sqrtSigma2T;
            double dminus12 = (lnRoverK - .5 * variance) / sqrtSigma2T;

            CumulativeNormalDistribution cumulativeOfNormal = new CumulativeNormalDistribution();
            double N32 = cumulativeOfNormal.value(((int)optionType) * d32);
            double N12 = cumulativeOfNormal.value(((int)optionType) * d12);
            double Nminus12 = cumulativeOfNormal.value(((int)optionType) * dminus12);

            price += ((int)optionType) * firstDerivativeOfGAtForwardValue * annuity_ * swapRateValue_ * (swapRateValue_ * Math.Exp(variance) * N32 - (swapRateValue_ + strike) * N12 + strike * Nminus12);
            price *= coupon_.accrualPeriod();
            return price;
        }
Beispiel #22
0
        //Hagan, 3.5b, 3.5c
        protected override double optionletPrice(Option.Type optionType, double strike)
        {
            double variance = swaptionVolatility().link.blackVariance(fixingDate_, swapTenor_, swapRateValue_);
            double firstDerivativeOfGAtForwardValue = gFunction_.firstDerivative(swapRateValue_);
            double price = 0;

            double CK = vanillaOptionPricer_.value(strike, optionType, annuity_);

            price += (discount_ / annuity_) * CK;
            double sqrtSigma2T = Math.Sqrt(variance);
            double lnRoverK    = Math.Log(swapRateValue_ / strike);
            double d32         = (lnRoverK + 1.5 * variance) / sqrtSigma2T;
            double d12         = (lnRoverK + .5 * variance) / sqrtSigma2T;
            double dminus12    = (lnRoverK - .5 * variance) / sqrtSigma2T;

            CumulativeNormalDistribution cumulativeOfNormal = new CumulativeNormalDistribution();
            double N32      = cumulativeOfNormal.value(((int)optionType) * d32);
            double N12      = cumulativeOfNormal.value(((int)optionType) * d12);
            double Nminus12 = cumulativeOfNormal.value(((int)optionType) * dminus12);

            price += ((int)optionType) * firstDerivativeOfGAtForwardValue * annuity_ * swapRateValue_ * (swapRateValue_ * Math.Exp(variance) * N32 - (swapRateValue_ + strike) * N12 + strike * Nminus12);
            price *= coupon_.accrualPeriod();
            return(price);
        }
Beispiel #23
0
        // public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount = 1.0) {
        public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount) {
            strike_ = payoff.strike();
            forward_ = forward;
            stdDev_ = stdDev;
            discount_ = discount;
            variance_ = stdDev*stdDev;

            if (!(forward>0.0))
                throw new ApplicationException("positive forward value required: " + forward + " not allowed");

            if (!(stdDev>=0.0))
                throw new ApplicationException("non-negative standard deviation required: " + stdDev + " not allowed");

            if (!(discount>0.0))
                throw new ApplicationException("positive discount required: " + discount + " not allowed");

            if (stdDev_>=Const.QL_EPSILON) {
                if (strike_==0.0) {
                    n_d1_ = 0.0;
                    n_d2_ = 0.0;
                    cum_d1_ = 1.0;
                    cum_d2_= 1.0;
                } else {
                    D1_ = Math.Log(forward/strike_)/stdDev_ + 0.5*stdDev_;
                    D2_ = D1_-stdDev_;
                    CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                    cum_d1_ = f.value(D1_);
                    cum_d2_= f.value(D2_);
                    n_d1_ = f.derivative(D1_);
                    n_d2_ = f.derivative(D2_);
                }
            } else {
                if (forward>strike_) {
                    cum_d1_ = 1.0;
                    cum_d2_= 1.0;
                } else {
                    cum_d1_ = 0.0;
                    cum_d2_= 0.0;
                }
                n_d1_ = 0.0;
                n_d2_ = 0.0;
            }

            X_ = strike_;
            DXDstrike_ = 1.0;

            // the following one will probably disappear as soon as
            // super-share will be properly handled
            DXDs_ = 0.0;

            // this part is always executed.
            // in case of plain-vanilla payoffs, it is also the only part
            // which is executed.
            switch (payoff.optionType()) {
                case Option.Type.Call:
                    alpha_     =  cum_d1_;//  N(d1)
                    DalphaDd1_ =    n_d1_;//  n(d1)
                    beta_      = -cum_d2_;// -N(d2)
                    DbetaDd2_  = -  n_d2_;// -n(d2)
                    break;
                case Option.Type.Put:
                    alpha_     = -1.0+cum_d1_;// -N(-d1)
                    DalphaDd1_ =        n_d1_;//  n( d1)
                    beta_      =  1.0-cum_d2_;//  N(-d2)
                    DbetaDd2_  =     -  n_d2_;// -n( d2)
                    break;
                default:
                    throw new ArgumentException("invalid option type");
            }

            // now dispatch on type.

            Calculator calc = new Calculator(this);
            payoff.accept(calc);
        }
Beispiel #24
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 double payoffAtExpiry(double spot, double variance, double discount)
        {
            double dividendDiscount = process_.dividendYield().link.discount(exercise_.lastDate());

            Utils.QL_REQUIRE(spot > 0.0, () => "positive spot value required");
            Utils.QL_REQUIRE(discount > 0.0, () => "positive discount required");
            Utils.QL_REQUIRE(dividendDiscount > 0.0, () => "positive dividend discount required");
            Utils.QL_REQUIRE(variance >= 0.0, () => "negative variance not allowed");

            Option.Type type    = payoff_.optionType();
            double      strike  = payoff_.strike();
            double?     barrier = arguments_.barrier;

            Utils.QL_REQUIRE(barrier > 0.0, () => "positive barrier value required");
            Barrier.Type barrierType = arguments_.barrierType;

            double stdDev = Math.Sqrt(variance);
            double mu     = Math.Log(dividendDiscount / discount) / variance - 0.5;
            double K      = 0;

            // binary cash-or-nothing payoff?
            CashOrNothingPayoff coo = payoff_ as CashOrNothingPayoff;

            if (coo != null)
            {
                K = coo.cashPayoff();
            }

            // binary asset-or-nothing payoff?
            AssetOrNothingPayoff aoo = payoff_ as AssetOrNothingPayoff;

            if (aoo != null)
            {
                mu += 1.0;
                K   = spot * dividendDiscount / discount; // forward
            }

            double log_S_X   = Math.Log(spot / strike);
            double log_S_H   = Math.Log(spot / barrier.GetValueOrDefault());
            double log_H_S   = Math.Log(barrier.GetValueOrDefault() / spot);
            double log_H2_SX = Math.Log(barrier.GetValueOrDefault() * barrier.GetValueOrDefault() / (spot * strike));
            double H_S_2mu   = Math.Pow(barrier.GetValueOrDefault() / spot, 2 * mu);

            double eta = (barrierType == Barrier.Type.DownIn ||
                          barrierType == Barrier.Type.DownOut ? 1.0 : -1.0);
            double phi = (type == Option.Type.Call ? 1.0 : -1.0);

            double x1, x2, y1, y2;
            double cum_x1, cum_x2, cum_y1, cum_y2;

            if (variance >= Const.QL_EPSILON)
            {
                // we calculate using mu*stddev instead of (mu+1)*stddev
                // because cash-or-nothing don't need it. asset-or-nothing
                // mu is really mu+1
                x1 = phi * (log_S_X / stdDev + mu * stdDev);
                x2 = phi * (log_S_H / stdDev + mu * stdDev);
                y1 = eta * (log_H2_SX / stdDev + mu * stdDev);
                y2 = eta * (log_H_S / stdDev + mu * stdDev);

                CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                cum_x1 = f.value(x1);
                cum_x2 = f.value(x2);
                cum_y1 = f.value(y1);
                cum_y2 = f.value(y2);
            }
            else
            {
                if (log_S_X > 0)
                {
                    cum_x1 = 1.0;
                }
                else
                {
                    cum_x1 = 0.0;
                }
                if (log_S_H > 0)
                {
                    cum_x2 = 1.0;
                }
                else
                {
                    cum_x2 = 0.0;
                }
                if (log_H2_SX > 0)
                {
                    cum_y1 = 1.0;
                }
                else
                {
                    cum_y1 = 0.0;
                }
                if (log_H_S > 0)
                {
                    cum_y2 = 1.0;
                }
                else
                {
                    cum_y2 = 0.0;
                }
            }

            double alpha = 0;

            switch (barrierType)
            {
            case Barrier.Type.DownIn:
                if (type == Option.Type.Call)
                {
                    // down-in and call
                    if (strike >= barrier)
                    {
                        // B3 (eta=1, phi=1)
                        alpha = H_S_2mu * cum_y1;
                    }
                    else
                    {
                        // B1-B2+B4 (eta=1, phi=1)
                        alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2;
                    }
                }
                else
                {
                    // down-in and put
                    if (strike >= barrier)
                    {
                        // B2-B3+B4 (eta=1, phi=-1)
                        alpha = cum_x2 + H_S_2mu * (-cum_y1 + cum_y2);
                    }
                    else
                    {
                        // B1 (eta=1, phi=-1)
                        alpha = cum_x1;
                    }
                }
                break;

            case Barrier.Type.UpIn:
                if (type == Option.Type.Call)
                {
                    // up-in and call
                    if (strike >= barrier)
                    {
                        // B1 (eta=-1, phi=1)
                        alpha = cum_x1;
                    }
                    else
                    {
                        // B2-B3+B4 (eta=-1, phi=1)
                        alpha = cum_x2 + H_S_2mu * (-cum_y1 + cum_y2);
                    }
                }
                else
                {
                    // up-in and put
                    if (strike >= barrier)
                    {
                        // B1-B2+B4 (eta=-1, phi=-1)
                        alpha = cum_x1 - cum_x2 + H_S_2mu * cum_y2;
                    }
                    else
                    {
                        // B3 (eta=-1, phi=-1)
                        alpha = H_S_2mu * cum_y1;
                    }
                }
                break;

            case Barrier.Type.DownOut:
                if (type == Option.Type.Call)
                {
                    // down-out and call
                    if (strike >= barrier)
                    {
                        // B1-B3 (eta=1, phi=1)
                        alpha = cum_x1 - H_S_2mu * cum_y1;
                    }
                    else
                    {
                        // B2-B4 (eta=1, phi=1)
                        alpha = cum_x2 - H_S_2mu * cum_y2;
                    }
                }
                else
                {
                    // down-out and put
                    if (strike >= barrier)
                    {
                        // B1-B2+B3-B4 (eta=1, phi=-1)
                        alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1 - cum_y2);
                    }
                    else
                    {
                        // always 0
                        alpha = 0;
                    }
                }
                break;

            case Barrier.Type.UpOut:
                if (type == Option.Type.Call)
                {
                    // up-out and call
                    if (strike >= barrier)
                    {
                        // always 0
                        alpha = 0;
                    }
                    else
                    {
                        // B1-B2+B3-B4 (eta=-1, phi=1)
                        alpha = cum_x1 - cum_x2 + H_S_2mu * (cum_y1 - cum_y2);
                    }
                }
                else
                {
                    // up-out and put
                    if (strike >= barrier)
                    {
                        // B2-B4 (eta=-1, phi=-1)
                        alpha = cum_x2 - H_S_2mu * cum_y2;
                    }
                    else
                    {
                        // B1-B3 (eta=-1, phi=-1)
                        alpha = cum_x1 - H_S_2mu * cum_y1;
                    }
                }
                break;

            default:
                Utils.QL_FAIL("invalid barrier type");
                break;
            }

            return(discount * K * alpha);
        }
        // critical commodity price
        //public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount,
        //                                   double variance, double tolerance = 1e-6);
        public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount,
                                           double variance, double tolerance)
        {
            // Calculation of seed value, Si
            double n  = 2.0 * Math.Log(dividendDiscount / riskFreeDiscount) / (variance);
            double m  = -2.0 * Math.Log(riskFreeDiscount) / (variance);
            double bT = Math.Log(dividendDiscount / riskFreeDiscount);

            double qu, Su, h, Si;

            switch (payoff.optionType())
            {
            case Option.Type.Call:
                qu = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
                Su = payoff.strike() / (1.0 - 1.0 / qu);
                h  = -(bT + 2.0 * Math.Sqrt(variance)) * payoff.strike() /
                     (Su - payoff.strike());
                Si = payoff.strike() + (Su - payoff.strike()) *
                     (1.0 - Math.Exp(h));
                break;

            case Option.Type.Put:
                qu = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4.0 * m)) / 2.0;
                Su = payoff.strike() / (1.0 - 1.0 / qu);
                h  = (bT - 2.0 * Math.Sqrt(variance)) * payoff.strike() /
                     (payoff.strike() - Su);
                Si = Su + (payoff.strike() - Su) * Math.Exp(h);
                break;

            default:
                throw new ArgumentException("unknown option type");
            }


            // Newton Raphson algorithm for finding critical price Si
            double Q, LHS, RHS, bi;
            double forwardSi = Si * dividendDiscount / riskFreeDiscount;
            double d1        = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance) /
                               Math.Sqrt(variance);
            CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
            double K = (riskFreeDiscount != 1.0 ? -2.0 * Math.Log(riskFreeDiscount) /
                        (variance * (1.0 - riskFreeDiscount)) : 0.0);
            double temp = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                                             forwardSi, Math.Sqrt(variance)) * riskFreeDiscount;

            switch (payoff.optionType())
            {
            case Option.Type.Call:
                Q   = (-(n - 1.0) + Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2;
                LHS = Si - payoff.strike();
                RHS = temp + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q;
                bi  = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q) +
                      (1 - dividendDiscount *
                       cumNormalDist.derivative(d1) / Math.Sqrt(variance)) / Q;
                while (Math.Abs(LHS - RHS) / payoff.strike() > tolerance)
                {
                    Si        = (payoff.strike() + RHS - bi * Si) / (1 - bi);
                    forwardSi = Si * dividendDiscount / riskFreeDiscount;
                    d1        = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance)
                                / Math.Sqrt(variance);
                    LHS = Si - payoff.strike();
                    double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                                                      forwardSi, Math.Sqrt(variance)) * riskFreeDiscount;
                    RHS = temp2 + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q;
                    bi  = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q)
                          + (1 - dividendDiscount *
                             cumNormalDist.derivative(d1) / Math.Sqrt(variance))
                          / Q;
                }
                break;

            case Option.Type.Put:
                Q   = (-(n - 1.0) - Math.Sqrt(((n - 1.0) * (n - 1.0)) + 4 * K)) / 2;
                LHS = payoff.strike() - Si;
                RHS = temp - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q;
                bi  = -dividendDiscount *cumNormalDist.value(-d1) * (1 - 1 / Q)
                      - (1 + dividendDiscount * cumNormalDist.derivative(-d1)
                         / Math.Sqrt(variance)) / Q;

                while (Math.Abs(LHS - RHS) / payoff.strike() > tolerance)
                {
                    Si        = (payoff.strike() - RHS + bi * Si) / (1 + bi);
                    forwardSi = Si * dividendDiscount / riskFreeDiscount;
                    d1        = (Math.Log(forwardSi / payoff.strike()) + 0.5 * variance)
                                / Math.Sqrt(variance);
                    LHS = payoff.strike() - Si;
                    double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                                                      forwardSi, Math.Sqrt(variance)) * riskFreeDiscount;
                    RHS = temp2 - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q;
                    bi  = -dividendDiscount *cumNormalDist.value(-d1) * (1 - 1 / Q)
                          - (1 + dividendDiscount * cumNormalDist.derivative(-d1)
                             / Math.Sqrt(variance)) / Q;
                }
                break;

            default:
                throw new ArgumentException("unknown option type");
            }

            return(Si);
        }
        // critical commodity price
        //public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount,
        //                                   double variance, double tolerance = 1e-6);
        public static double criticalPrice(StrikedTypePayoff payoff, double riskFreeDiscount, double dividendDiscount,
                                           double variance, double tolerance)
        {
            // Calculation of seed value, Si
            double n= 2.0*Math.Log(dividendDiscount/riskFreeDiscount)/(variance);
            double m=-2.0*Math.Log(riskFreeDiscount)/(variance);
            double bT = Math.Log(dividendDiscount/riskFreeDiscount);

            double qu, Su, h, Si;
            switch (payoff.optionType()) {
                case Option.Type.Call:
                    qu = (-(n-1.0) + Math.Sqrt(((n-1.0)*(n-1.0)) + 4.0*m))/2.0;
                    Su = payoff.strike() / (1.0 - 1.0/qu);
                    h = -(bT + 2.0*Math.Sqrt(variance)) * payoff.strike() /
                        (Su - payoff.strike());
                    Si = payoff.strike() + (Su - payoff.strike()) *
                        (1.0 - Math.Exp(h));
                    break;
                case Option.Type.Put:
                    qu = (-(n-1.0) - Math.Sqrt(((n-1.0)*(n-1.0)) + 4.0*m))/2.0;
                    Su = payoff.strike() / (1.0 - 1.0/qu);
                    h = (bT - 2.0*Math.Sqrt(variance)) * payoff.strike() /
                        (payoff.strike() - Su);
                    Si = Su + (payoff.strike() - Su) * Math.Exp(h);
                    break;
                default:
                    throw new ArgumentException("unknown option type");
            }

            // Newton Raphson algorithm for finding critical price Si
            double Q, LHS, RHS, bi;
            double forwardSi = Si * dividendDiscount / riskFreeDiscount;
            double d1 = (Math.Log(forwardSi/payoff.strike()) + 0.5*variance) /
                Math.Sqrt(variance);
            CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
            double K = (riskFreeDiscount!=1.0 ? -2.0*Math.Log(riskFreeDiscount)/
                (variance*(1.0-riskFreeDiscount)) : 0.0);
            double temp = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                    forwardSi, Math.Sqrt(variance))*riskFreeDiscount;
            switch (payoff.optionType()) {
                case Option.Type.Call:
                    Q = (-(n-1.0) + Math.Sqrt(((n-1.0)*(n-1.0)) + 4 * K)) / 2;
                    LHS = Si - payoff.strike();
                    RHS = temp + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q;
                    bi = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q) +
                        (1 - dividendDiscount *
                         cumNormalDist.derivative(d1) / Math.Sqrt(variance)) / Q;
                    while (Math.Abs(LHS - RHS)/payoff.strike() > tolerance) {
                        Si = (payoff.strike() + RHS - bi * Si) / (1 - bi);
                        forwardSi = Si * dividendDiscount / riskFreeDiscount;
                        d1 = (Math.Log(forwardSi/payoff.strike())+0.5*variance)
                            /Math.Sqrt(variance);
                        LHS = Si - payoff.strike();
                        double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                            forwardSi, Math.Sqrt(variance))*riskFreeDiscount;
                        RHS = temp2 + (1 - dividendDiscount * cumNormalDist.value(d1)) * Si / Q;
                        bi = dividendDiscount * cumNormalDist.value(d1) * (1 - 1 / Q)
                            + (1 - dividendDiscount *
                               cumNormalDist.derivative(d1) / Math.Sqrt(variance))
                            / Q;
                    }
                    break;
                case Option.Type.Put:
                    Q = (-(n-1.0) - Math.Sqrt(((n-1.0)*(n-1.0)) + 4 * K)) / 2;
                    LHS = payoff.strike() - Si;
                    RHS = temp - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q;
                    bi = -dividendDiscount * cumNormalDist.value(-d1) * (1 - 1 / Q)
                        - (1 + dividendDiscount * cumNormalDist.derivative(-d1)
                           / Math.Sqrt(variance)) / Q;
                    while (Math.Abs(LHS - RHS)/payoff.strike() > tolerance) {
                        Si = (payoff.strike() - RHS + bi * Si) / (1 + bi);
                        forwardSi = Si * dividendDiscount / riskFreeDiscount;
                        d1 = (Math.Log(forwardSi/payoff.strike())+0.5*variance)
                            /Math.Sqrt(variance);
                        LHS = payoff.strike() - Si;
                        double temp2 = Utils.blackFormula(payoff.optionType(), payoff.strike(),
                            forwardSi, Math.Sqrt(variance))*riskFreeDiscount;
                        RHS = temp2 - (1 - dividendDiscount * cumNormalDist.value(-d1)) * Si / Q;
                        bi = -dividendDiscount * cumNormalDist.value(-d1) * (1 - 1 / Q)
                            - (1 + dividendDiscount * cumNormalDist.derivative(-d1)
                               / Math.Sqrt(variance)) / Q;
                    }
                    break;
                default:
                    throw new ArgumentException("unknown option type");
            }

            return Si;
        }
Beispiel #28
0
        // public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount = 1.0) {
        public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount)
        {
            strike_   = payoff.strike();
            forward_  = forward;
            stdDev_   = stdDev;
            discount_ = discount;
            variance_ = stdDev * stdDev;

            if (!(forward > 0.0))
            {
                throw new ApplicationException("positive forward value required: " + forward + " not allowed");
            }

            if (!(stdDev >= 0.0))
            {
                throw new ApplicationException("non-negative standard deviation required: " + stdDev + " not allowed");
            }

            if (!(discount > 0.0))
            {
                throw new ApplicationException("positive discount required: " + discount + " not allowed");
            }

            if (stdDev_ >= Const.QL_Epsilon)
            {
                if (strike_ == 0.0)
                {
                    n_d1_   = 0.0;
                    n_d2_   = 0.0;
                    cum_d1_ = 1.0;
                    cum_d2_ = 1.0;
                }
                else
                {
                    D1_ = Math.Log(forward / strike_) / stdDev_ + 0.5 * stdDev_;
                    D2_ = D1_ - stdDev_;
                    CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                    cum_d1_ = f.value(D1_);
                    cum_d2_ = f.value(D2_);
                    n_d1_   = f.derivative(D1_);
                    n_d2_   = f.derivative(D2_);
                }
            }
            else
            {
                if (forward > strike_)
                {
                    cum_d1_ = 1.0;
                    cum_d2_ = 1.0;
                }
                else
                {
                    cum_d1_ = 0.0;
                    cum_d2_ = 0.0;
                }
                n_d1_ = 0.0;
                n_d2_ = 0.0;
            }

            X_         = strike_;
            DXDstrike_ = 1.0;

            // the following one will probably disappear as soon as
            // super-share will be properly handled
            DXDs_ = 0.0;

            // this part is always executed.
            // in case of plain-vanilla payoffs, it is also the only part
            // which is executed.
            switch (payoff.optionType())
            {
            case Option.Type.Call:
                alpha_     = cum_d1_;     //  N(d1)
                DalphaDd1_ = n_d1_;       //  n(d1)
                beta_      = -cum_d2_;    // -N(d2)
                DbetaDd2_  = -n_d2_;      // -n(d2)
                break;

            case Option.Type.Put:
                alpha_     = -1.0 + cum_d1_;  // -N(-d1)
                DalphaDd1_ = n_d1_;           //  n( d1)
                beta_      = 1.0 - cum_d2_;   //  N(-d2)
                DbetaDd2_  = -n_d2_;          // -n( d2)
                break;

            default:
                throw new ArgumentException("invalid option type");
            }

            // now dispatch on type.

            Calculator calc = new Calculator(this);

            payoff.accept(calc);
        }
        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());
        }
Beispiel #30
0
        //! gaussian-assumption Shortfall (observations below target)
        public double gaussianShortfall(double target)
        {
            CumulativeNormalDistribution gIntegral = new CumulativeNormalDistribution(this.mean(), this.standardDeviation());

            return(gIntegral.value(target));
        }
Beispiel #31
0
            public double value(double x)
            {
                CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
                double temp = (x - mux_)/sigmax_;
                double txy = Math.Sqrt(1.0 - rhoxy_*rhoxy_);

                Vector lambda = new Vector(size_);
                int i;
                for (i=0; i<size_; i++) {
                double tau = (i==0 ? t_[0] - T_ : t_[i] - t_[i-1]);
                double c = (i==size_-1 ? (1.0+rate_*tau) : rate_*tau);
                lambda[i] = c*A_[i]*Math.Exp(-Ba_[i]*x);
                }

                //SolvingFunction function = new SolvingFunction(lambda, Bb_);
                //verifier le polymorphisme
                SolvingFunction function = new SolvingFunction(lambda, Bb_);
                Brent s1d = new Brent();
                s1d.setMaxEvaluations(1000);
                double yb = s1d.solve(function, 1e-6, 0.00, -100.0, 100.0);

                double h1 = (yb - muy_)/(sigmay_*txy) -
                rhoxy_*(x  - mux_)/(sigmax_*txy);
                double value = phi.value(-w_*h1);

                for (i=0; i<size_; i++) {
                double h2 = h1 +
                    Bb_[i]*sigmay_*Math.Sqrt(1.0-rhoxy_*rhoxy_);
                double kappa = - Bb_[i] *
                    (muy_ - 0.5*txy*txy*sigmay_*sigmay_*Bb_[i] +
                     rhoxy_*sigmay_*(x-mux_)/sigmax_);
                value -= lambda[i] *Math.Exp(kappa)*phi.value(-w_*h2);
                }

                return Math.Exp(-0.5*temp*temp)*value/
                (sigmax_*Math.Sqrt(2.0*QLNet.Const.M_PI));
            }
Beispiel #32
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());
        }
Beispiel #33
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"
        }
Beispiel #34
0
        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);
        }
        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();
                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 = -2.0*Math.Log(riskFreeDiscount)/
                    (variance*(1.0-riskFreeDiscount));
                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:
                      throw new ApplicationException("unknown option type");
                }
            } // end of "early exercise can be optimal"
        }
        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();
                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 = -2.0 * Math.Log(riskFreeDiscount) /
                           (variance * (1.0 - riskFreeDiscount));
                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:
                    throw new ApplicationException("unknown option type");
                }
            } // end of "early exercise can be optimal"
        }
Beispiel #37
0
        //private double DXDstrike_;

        public AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff)
        {
            spot_             = spot;
            discount_         = discount;
            dividendDiscount_ = dividendDiscount;
            variance_         = variance;

            if (!(spot_ > 0.0))
            {
                throw new ApplicationException("positive spot_ value required");
            }

            forward_ = spot_ * dividendDiscount_ / discount_;

            if (!(discount_ > 0.0))
            {
                throw new ApplicationException("positive discount required");
            }

            if (!(dividendDiscount_ > 0.0))
            {
                throw new ApplicationException("positive dividend discount_ required");
            }

            if (!(variance_ >= 0.0))
            {
                throw new ApplicationException("negative variance_ not allowed");
            }

            stdDev_ = Math.Sqrt(variance_);

            Option.Type type = payoff.optionType();
            strike_ = payoff.strike();


            mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5;

            // binary cash-or-nothing payoff?
            CashOrNothingPayoff coo = payoff as CashOrNothingPayoff;

            if (coo != null)
            {
                K_ = coo.cashPayoff();
                //DKDstrike_ = 0.0;
            }

            // binary asset-or-nothing payoff?
            AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff;

            if (aoo != null)
            {
                K_ = forward_;
                //DKDstrike_ = 0.0;
                mu_ += 1.0;
            }


            log_H_S_ = Math.Log(strike_ / spot_);

            double n_d1;
            double n_d2;
            double cum_d1_;
            double cum_d2_;

            if (variance_ >= Const.QL_Epsilon)
            {
                D1_ = log_H_S_ / stdDev_ + mu_ * stdDev_;
                D2_ = D1_ - 2.0 * mu_ * stdDev_;
                CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                cum_d1_ = f.value(D1_);
                cum_d2_ = f.value(D2_);
                n_d1    = f.derivative(D1_);
                n_d2    = f.derivative(D2_);
            }
            else
            {
                if (log_H_S_ > 0)
                {
                    cum_d1_ = 1.0;
                    cum_d2_ = 1.0;
                }
                else
                {
                    cum_d1_ = 0.0;
                    cum_d2_ = 0.0;
                }
                n_d1 = 0.0;
                n_d2 = 0.0;
            }


            switch (type)
            {
            // up-and-in cash-(at-hit)-or-nothing option
            // a.k.a. american call with cash-or-nothing payoff
            case Option.Type.Call:
                if (strike_ > spot_)
                {
                    alpha_     = 1.0 - cum_d2_; // N(-d2)
                    DalphaDd1_ = -n_d2;         // -n( d2)
                    beta_      = 1.0 - cum_d1_; // N(-d1)
                    DbetaDd2_  = -n_d1;         // -n( d1)
                }
                else
                {
                    alpha_     = 0.5;
                    DalphaDd1_ = 0.0;
                    beta_      = 0.5;
                    DbetaDd2_  = 0.0;
                }
                break;

            // down-and-in cash-(at-hit)-or-nothing option
            // a.k.a. american put with cash-or-nothing payoff
            case Option.Type.Put:
                if (strike_ < spot_)
                {
                    alpha_     = cum_d2_; // N(d2)
                    DalphaDd1_ = n_d2;    // n(d2)
                    beta_      = cum_d1_; // N(d1)
                    DbetaDd2_  = n_d1;    // n(d1)
                }
                else
                {
                    alpha_     = 0.5;
                    DalphaDd1_ = 0.0;
                    beta_      = 0.5;
                    DbetaDd2_  = 0.0;
                }
                break;

            default:
                throw new ApplicationException("invalid option type");
            }


            inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_);
            if (inTheMoney_)
            {
                Y_ = 1.0;
                X_ = 1.0;
                //DYDstrike_ = 0.0;
                //DXDstrike_ = 0.0;
            }
            else
            {
                Y_ = 1.0;
                X_ = Math.Pow((double)(strike_ / spot_), (double)(2.0 * mu_));
                //            DXDstrike_ = ......;
            }
        }
Beispiel #38
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");
             }
        }
        public AmericanPayoffAtHit(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) {
            spot_ = spot;
            discount_ = discount;
            dividendDiscount_ = dividendDiscount;
            variance_ = variance;

            if (!(spot_ > 0.0))
                throw new ApplicationException("positive spot value required");

            if (!(discount_ > 0.0))
                throw new ApplicationException("positive discount required");

            if (!(dividendDiscount_ > 0.0))
                throw new ApplicationException("positive dividend discount required");

            if (!(variance_ >= 0.0))
                throw new ApplicationException("negative variance not allowed");

            stdDev_ = Math.Sqrt(variance_);

            Option.Type type = payoff.optionType();
            strike_ = payoff.strike();


            log_H_S_ = Math.Log(strike_ / spot_);

            double n_d1;
            double n_d2;
            double cum_d1_;
            double cum_d2_;
            if (variance_ >= Const.QL_Epsilon) {
                if (discount_ == 0.0 && dividendDiscount_ == 0.0) {
                    mu_ = -0.5;
                    lambda_ = 0.5;
                } else if (discount_ == 0.0) {
                    throw new ApplicationException("null discount not handled yet");
                } else {
                    mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5;
                    lambda_ = Math.Sqrt(mu_ * mu_ - 2.0 * Math.Log(discount_) / variance_);
                }
                D1_ = log_H_S_ / stdDev_ + lambda_ * stdDev_;
                D2_ = D1_ - 2.0 * lambda_ * stdDev_;
                CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                cum_d1_ = f.value(D1_);
                cum_d2_ = f.value(D2_);
                n_d1 = f.derivative(D1_);
                n_d2 = f.derivative(D2_);
            } else {
                // not tested yet
                mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5;
                lambda_ = Math.Sqrt(mu_ * mu_ - 2.0 * Math.Log(discount_) / variance_);
                if (log_H_S_ > 0) {
                    cum_d1_ = 1.0;
                    cum_d2_ = 1.0;
                } else {
                    cum_d1_ = 0.0;
                    cum_d2_ = 0.0;
                }
                n_d1 = 0.0;
                n_d2 = 0.0;
            }


            switch (type) {
                // up-and-in cash-(at-hit)-or-nothing option
                // a.k.a. american call with cash-or-nothing payoff
                case Option.Type.Call:
                    if (strike_ > spot_) {
                        alpha_ = 1.0 - cum_d1_; // N(-d1)
                        DalphaDd1_ = -n_d1; // -n( d1)
                        beta_ = 1.0 - cum_d2_; // N(-d2)
                        DbetaDd2_ = -n_d2; // -n( d2)
                    } else {
                        alpha_ = 0.5;
                        DalphaDd1_ = 0.0;
                        beta_ = 0.5;
                        DbetaDd2_ = 0.0;
                    }
                    break;
                // down-and-in cash-(at-hit)-or-nothing option
                // a.k.a. american put with cash-or-nothing payoff
                case Option.Type.Put:
                    if (strike_ < spot_) {
                        alpha_ = cum_d1_; // N(d1)
                        DalphaDd1_ = n_d1; // n(d1)
                        beta_ = cum_d2_; // N(d2)
                        DbetaDd2_ = n_d2; // n(d2)
                    } else {
                        alpha_ = 0.5;
                        DalphaDd1_ = 0.0;
                        beta_ = 0.5;
                        DbetaDd2_ = 0.0;
                    }
                    break;
                default:
                    throw new ApplicationException("invalid option type");
            }


            muPlusLambda_ = mu_ + lambda_;
            muMinusLambda_ = mu_ - lambda_;
            inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_);

            if (inTheMoney_) {
                forward_ = 1.0;
                X_ = 1.0;
                DXDstrike_ = 0.0;
            } else {
                forward_ = Math.Pow(strike_ / spot_, muPlusLambda_);
                X_ = Math.Pow(strike_ / spot_, muMinusLambda_);
                //            DXDstrike_ = ......;
            }


            // Binary Cash-Or-Nothing payoff?
            CashOrNothingPayoff coo = payoff as CashOrNothingPayoff;
            if (coo != null) {
                K_ = coo.cashPayoff();
                DKDstrike_ = 0.0;
            }

            // Binary Asset-Or-Nothing payoff?
            AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff;

            if (aoo != null) {
                if (inTheMoney_) {
                    K_ = spot_;
                    DKDstrike_ = 0.0;
                } else {
                    K_ = aoo.strike();
                    DKDstrike_ = 1.0;
                }
            }
        }
        //private double DXDstrike_;

        public AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff) {
            spot_ = spot;
            discount_ = discount;
            dividendDiscount_ = dividendDiscount;
            variance_ = variance;

            if (!(spot_ > 0.0))
                throw new ApplicationException("positive spot_ value required");

            forward_ = spot_ * dividendDiscount_ / discount_;

            if (!(discount_ > 0.0))
                throw new ApplicationException("positive discount required");

            if (!(dividendDiscount_ > 0.0))
                throw new ApplicationException("positive dividend discount_ required");

            if (!(variance_ >= 0.0))
                throw new ApplicationException("negative variance_ not allowed");

            stdDev_ = Math.Sqrt(variance_);

            Option.Type type = payoff.optionType();
            strike_ = payoff.strike();


            mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5;

            // binary cash-or-nothing payoff?
            CashOrNothingPayoff coo = payoff as CashOrNothingPayoff;
            if (coo != null) {
                K_ = coo.cashPayoff();
                //DKDstrike_ = 0.0;
            }

            // binary asset-or-nothing payoff?
            AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff;
            if (aoo != null) {
                K_ = forward_;
                //DKDstrike_ = 0.0;
                mu_ += 1.0;
            }


            log_H_S_ = Math.Log(strike_ / spot_);

            double n_d1;
            double n_d2;
            double cum_d1_;
            double cum_d2_;
            if (variance_ >= Const.QL_EPSILON) {
                D1_ = log_H_S_ / stdDev_ + mu_ * stdDev_;
                D2_ = D1_ - 2.0 * mu_ * stdDev_;
                CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                cum_d1_ = f.value(D1_);
                cum_d2_ = f.value(D2_);
                n_d1 = f.derivative(D1_);
                n_d2 = f.derivative(D2_);
            } else {
                if (log_H_S_ > 0) {
                    cum_d1_ = 1.0;
                    cum_d2_ = 1.0;
                } else {
                    cum_d1_ = 0.0;
                    cum_d2_ = 0.0;
                }
                n_d1 = 0.0;
                n_d2 = 0.0;
            }


            switch (type) {
                // up-and-in cash-(at-hit)-or-nothing option
                // a.k.a. american call with cash-or-nothing payoff
                case Option.Type.Call:
                    if (strike_ > spot_) {
                        alpha_ = 1.0 - cum_d2_; // N(-d2)
                        DalphaDd1_ = -n_d2; // -n( d2)
                        beta_ = 1.0 - cum_d1_; // N(-d1)
                        DbetaDd2_ = -n_d1; // -n( d1)
                    } else {
                        alpha_ = 0.5;
                        DalphaDd1_ = 0.0;
                        beta_ = 0.5;
                        DbetaDd2_ = 0.0;
                    }
                    break;
                // down-and-in cash-(at-hit)-or-nothing option
                // a.k.a. american put with cash-or-nothing payoff
                case Option.Type.Put:
                    if (strike_ < spot_) {
                        alpha_ = cum_d2_; // N(d2)
                        DalphaDd1_ = n_d2; // n(d2)
                        beta_ = cum_d1_; // N(d1)
                        DbetaDd2_ = n_d1; // n(d1)
                    } else {
                        alpha_ = 0.5;
                        DalphaDd1_ = 0.0;
                        beta_ = 0.5;
                        DbetaDd2_ = 0.0;
                    }
                    break;
                default:
                    throw new ApplicationException("invalid option type");
            }


            inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) || (type == Option.Type.Put && strike_ > spot_);
            if (inTheMoney_) {
                Y_ = 1.0;
                X_ = 1.0;
                //DYDstrike_ = 0.0;
                //DXDstrike_ = 0.0;
            } else {
                Y_ = 1.0;
                X_ = Math.Pow((double)(strike_ / spot_), (double)(2.0 * mu_));
                //            DXDstrike_ = ......;
            }

        }
Beispiel #41
0
        public override Vector evolve(double t0, Vector x0, double dt, Vector dw)
        {
            Vector retVal = new Vector(2);
            double vol, vol2, mu, nu, dy;

            double sdt    = Math.Sqrt(dt);
            double sqrhov = Math.Sqrt(1.0 - rho_ * rho_);

            switch (discretization_)
            {
            // For the definition of PartialTruncation, FullTruncation
            // and Reflection  see Lord, R., R. Koekkoek and D. van Dijk (2006),
            // "A Comparison of biased simulation schemes for
            //  stochastic volatility models",
            // Working Paper, Tinbergen Institute
            case Discretization.PartialTruncation:
                vol  = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0;
                vol2 = sigma_ * vol;
                mu   = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - 0.5 * vol * vol;
                nu = kappa_ * (theta_ - x0[1]);

                retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt);
                retVal[1] = x0[1] + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]);
                break;

            case Discretization.FullTruncation:
                vol  = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0;
                vol2 = sigma_ * vol;
                mu   = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - 0.5 * vol * vol;
                nu = kappa_ * (theta_ - vol * vol);

                retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt);
                retVal[1] = x0[1] + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]);
                break;

            case Discretization.Reflection:
                vol  = Math.Sqrt(Math.Abs(x0[1]));
                vol2 = sigma_ * vol;
                mu   = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                       - 0.5 * vol * vol;
                nu = kappa_ * (theta_ - vol * vol);

                retVal[0] = x0[0] * Math.Exp(mu * dt + vol * dw[0] * sdt);
                retVal[1] = vol * vol
                            + nu * dt + vol2 * sdt * (rho_ * dw[0] + sqrhov * dw[1]);
                break;

            case Discretization.NonCentralChiSquareVariance:
                // use Alan Lewis trick to decorrelate the equity and the variance
                // process by using y(t)=x(t)-\frac{rho}{sigma}\nu(t)
                // and Ito's Lemma. Then use exact sampling for the variance
                // process. For further details please read the Wilmott thread
                // "QuantLib code is very high quality"
                vol = (x0[1] > 0.0) ? Math.Sqrt(x0[1]) : 0.0;
                mu  = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                      - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                      - 0.5 * vol * vol;

                retVal[1] = varianceDistribution(x0[1], dw[1], dt);
                dy        = (mu - rho_ / sigma_ * kappa_
                             * (theta_ - vol * vol)) * dt + vol * sqrhov * dw[0] * sdt;

                retVal[0] = x0[0] * Math.Exp(dy + rho_ / sigma_ * (retVal[1] - x0[1]));
                break;

            case Discretization.QuadraticExponential:
            case Discretization.QuadraticExponentialMartingale:
            {
                // for details of the quadratic exponential discretization scheme
                // see Leif Andersen,
                // Efficient Simulation of the Heston Stochastic Volatility Model
                double ex = Math.Exp(-kappa_ * dt);

                double m  = theta_ + (x0[1] - theta_) * ex;
                double s2 = x0[1] * sigma_ * sigma_ * ex / kappa_ * (1 - ex)
                            + theta_ * sigma_ * sigma_ / (2 * kappa_) * (1 - ex) * (1 - ex);
                double psi = s2 / (m * m);

                double g1 = 0.5;
                double g2 = 0.5;
                double k0 = -rho_ * kappa_ * theta_ * dt / sigma_;
                double k1 = g1 * dt * (kappa_ * rho_ / sigma_ - 0.5) - rho_ / sigma_;
                double k2 = g2 * dt * (kappa_ * rho_ / sigma_ - 0.5) + rho_ / sigma_;
                double k3 = g1 * dt * (1 - rho_ * rho_);
                double k4 = g2 * dt * (1 - rho_ * rho_);
                double A  = k2 + 0.5 * k4;

                if (psi < 1.5)
                {
                    double b2 = 2 / psi - 1 + Math.Sqrt(2 / psi * (2 / psi - 1));
                    double b  = Math.Sqrt(b2);
                    double a  = m / (1 + b2);

                    if (discretization_ == Discretization.QuadraticExponentialMartingale)
                    {
                        // martingale correction
                        Utils.QL_REQUIRE(A < 1 / (2 * a), () => "illegal value");
                        k0 = -A * b2 * a / (1 - 2 * A * a) + 0.5 * Math.Log(1 - 2 * A * a)
                             - (k1 + 0.5 * k3) * x0[1];
                    }
                    retVal[1] = a * (b + dw[1]) * (b + dw[1]);
                }
                else
                {
                    double p    = (psi - 1) / (psi + 1);
                    double beta = (1 - p) / m;

                    double u = new CumulativeNormalDistribution().value(dw[1]);

                    if (discretization_ == Discretization.QuadraticExponentialMartingale)
                    {
                        // martingale correction
                        Utils.QL_REQUIRE(A < beta, () => "illegal value");
                        k0 = -Math.Log(p + beta * (1 - p) / (beta - A)) - (k1 + 0.5 * k3) * x0[1];
                    }
                    retVal[1] = ((u <= p) ? 0.0 : Math.Log((1 - p) / (1 - u)) / beta);
                }

                mu = riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                     - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value();

                retVal[0] = x0[0] * Math.Exp(mu * dt + k0 + k1 * x0[1] + k2 * retVal[1]
                                             + Math.Sqrt(k3 * x0[1] + k4 * retVal[1]) * dw[0]);
            }
            break;

            case Discretization.BroadieKayaExactSchemeLobatto:
            case Discretization.BroadieKayaExactSchemeLaguerre:
            case Discretization.BroadieKayaExactSchemeTrapezoidal:
            {
                double nu_0 = x0[1];
                double nu_t = varianceDistribution(nu_0, dw[1], dt);

                double x = Math.Min(1.0 - Const.QL_EPSILON,
                                    Math.Max(0.0, new CumulativeNormalDistribution().value(dw[2])));

                cdf_nu_ds f   = new cdf_nu_ds(this, nu_0, nu_t, dt, discretization_);
                double    vds = new Brent().solve(f, 1e-5, -x, theta_ * dt, 0.1 * theta_ * dt);

                //double vds = new Brent().solve( boost::lambda::bind(&cdf_nu_ds, *this, boost::lambda::_1,
                //                               nu_0, nu_t, dt, discretization_)-x,
                //                               1e-5, theta_*dt, 0.1*theta_*dt);

                double vdw = (nu_t - nu_0 - kappa_ * theta_ * dt + kappa_ * vds) / sigma_;

                mu = (riskFreeRate_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()
                      - dividendYield_.link.forwardRate(t0, t0 + dt, Compounding.Continuous).value()) * dt
                     - 0.5 * vds + rho_ * vdw;

                double sig = Math.Sqrt((1 - rho_ * rho_) * vds);
                double s   = x0[0] * Math.Exp(mu + sig * dw[0]);

                retVal[0] = s;
                retVal[1] = nu_t;
            }
            break;

            default:
                Utils.QL_FAIL("unknown discretization schema");
                break;
            }

            return(retVal);
        }
Beispiel #42
0
        public static double blackFormulaStdDevDerivative( double strike,
            double forward,
            double stdDev,
            double discount,
            double displacement)
        {
            checkParameters( strike, forward, displacement );

            if ( stdDev < 0.0 )
                throw new ArgumentException( "stdDev (" + stdDev + ") must be non-negative" );

            if ( discount <= 0.0 )
                throw new ArgumentException( "discount (" + discount + ") must be positive" );

            forward = forward + displacement;
            strike = strike + displacement;

            if ( stdDev == 0.0  || strike == 0)
                    return 0.0;

            double d1 = Math.Log( forward / strike ) / stdDev + .5 * stdDev;
            CumulativeNormalDistribution phi = new CumulativeNormalDistribution();
            return discount * forward * phi.derivative( d1 );
        }
        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 AmericanPayoffAtExpiry(double spot, double discount, double dividendDiscount, double variance,
                                      StrikedTypePayoff payoff, bool knock_in = true)
        {
            spot_             = spot;
            discount_         = discount;
            dividendDiscount_ = dividendDiscount;
            variance_         = variance;
            knock_in_         = knock_in;

            Utils.QL_REQUIRE(spot_ > 0.0, () => "positive spot value required");
            Utils.QL_REQUIRE(discount_ > 0.0, () => "positive discount required");
            Utils.QL_REQUIRE(dividendDiscount_ > 0.0, () => "positive dividend discount required");
            Utils.QL_REQUIRE(variance_ >= 0.0, () => "negative variance not allowed");

            stdDev_ = Math.Sqrt(variance_);
            Option.Type type = payoff.optionType();
            strike_  = payoff.strike();
            forward_ = spot_ * dividendDiscount_ / discount_;

            mu_ = Math.Log(dividendDiscount_ / discount_) / variance_ - 0.5;

            // binary cash-or-nothing payoff?
            CashOrNothingPayoff coo = payoff as CashOrNothingPayoff;

            if (coo != null)
            {
                K_ = coo.cashPayoff();
            }

            // binary asset-or-nothing payoff?
            AssetOrNothingPayoff aoo = payoff as AssetOrNothingPayoff;

            if (aoo != null)
            {
                K_   = forward_;
                mu_ += 1.0;
            }


            log_H_S_ = Math.Log(strike_ / spot_);
            double log_S_H_ = Math.Log(spot_ / strike_);

            double eta = 0.0;
            double phi = 0.0;

            switch (type)
            {
            case Option.Type.Call:
                if (knock_in_)
                {
                    // up-and-in cash-(at-expiry)-or-nothing option
                    // a.k.a. american call with cash-or-nothing payoff
                    eta = -1.0;
                    phi = 1.0;
                }
                else
                {
                    // up-and-out cash-(at-expiry)-or-nothing option
                    eta = -1.0;
                    phi = -1.0;
                }
                break;

            case Option.Type.Put:
                if (knock_in_)
                {
                    // down-and-in cash-(at-expiry)-or-nothing option
                    // a.k.a. american put with cash-or-nothing payoff
                    eta = 1.0;
                    phi = -1.0;
                }
                else
                {
                    // down-and-out cash-(at-expiry)-or-nothing option
                    eta = 1.0;
                    phi = 1.0;
                }
                break;

            default:
                Utils.QL_FAIL("invalid option type");
                break;
            }


            if (variance_ >= Const.QL_EPSILON)
            {
                D1_ = phi * (log_S_H_ / stdDev_ + mu_ * stdDev_);
                D2_ = eta * (log_H_S_ / stdDev_ + mu_ * stdDev_);
                CumulativeNormalDistribution f = new CumulativeNormalDistribution();
                cum_d1_ = f.value(D1_);
                cum_d2_ = f.value(D2_);
                n_d1_   = f.derivative(D1_);
                n_d2_   = f.derivative(D2_);
            }
            else
            {
                if (log_S_H_ * phi > 0)
                {
                    cum_d1_ = 1.0;
                }
                else
                {
                    cum_d1_ = 0.0;
                }

                if (log_H_S_ * eta > 0)
                {
                    cum_d2_ = 1.0;
                }
                else
                {
                    cum_d2_ = 0.0;
                }

                n_d1_ = 0.0;
                n_d2_ = 0.0;
            }

            switch (type)
            {
            case Option.Type.Call:
                if (strike_ <= spot_)
                {
                    if (knock_in_)
                    {
                        // up-and-in cash-(at-expiry)-or-nothing option
                        // a.k.a. american call with cash-or-nothing payoff
                        cum_d1_ = 0.5;
                        cum_d2_ = 0.5;
                    }
                    else
                    {
                        // up-and-out cash-(at-expiry)-or-nothing option
                        // already knocked out
                        cum_d1_ = 0.0;
                        cum_d2_ = 0.0;
                    }
                    n_d1_ = 0.0;
                    n_d2_ = 0.0;
                }
                break;

            case Option.Type.Put:
                if (strike_ >= spot_)
                {
                    if (knock_in_)
                    {
                        // down-and-in cash-(at-expiry)-or-nothing option
                        // a.k.a. american put with cash-or-nothing payoff
                        cum_d1_ = 0.5;
                        cum_d2_ = 0.5;
                    }
                    else
                    {
                        // down-and-out cash-(at-expiry)-or-nothing option
                        // already knocked out
                        cum_d1_ = 0.0;
                        cum_d2_ = 0.0;
                    }
                    n_d1_ = 0.0;
                    n_d2_ = 0.0;
                }
                break;

            default:
                Utils.QL_FAIL("invalid option type");
                break;
            }


            inTheMoney_ = (type == Option.Type.Call && strike_ < spot_) ||
                          (type == Option.Type.Put && strike_ > spot_);
            if (inTheMoney_)
            {
                X_ = 1.0;
                Y_ = 1.0;
            }
            else
            {
                X_ = 1.0;
                if (cum_d2_ == 0.0)
                {
                    Y_ = 0.0; // check needed on some extreme cases
                }
                else
                {
                    Y_ = Math.Pow((strike_ / spot_), (2.0 * mu_));
                }
            }
            if (!knock_in_)
            {
                Y_ *= -1.0;
            }
        }