예제 #1
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);
        }
예제 #2
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_);
        }
예제 #3
0
        public AmericanPayoffAtHit(double spot, double discount, double dividendDiscount, double variance, StrikedTypePayoff payoff)
        {
            spot_             = spot;
            discount_         = discount;
            dividendDiscount_ = dividendDiscount;
            variance_         = variance;

            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();


            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_.IsEqual(0.0) && dividendDiscount_.IsEqual(0.0))
                {
                    mu_     = -0.5;
                    lambda_ = 0.5;
                }
                else if (discount_.IsEqual(0.0))
                {
                    Utils.QL_FAIL("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:
                Utils.QL_FAIL("invalid option type");
                break;
            }


            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;
            }
            else
            {
                forward_ = Math.Pow(strike_ / spot_, muPlusLambda_);
                X_       = Math.Pow(strike_ / spot_, muMinusLambda_);
            }


            // 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)
            {
                if (inTheMoney_)
                {
                    K_ = spot_;
                }
                else
                {
                    K_ = aoo.strike();
                }
            }
        }
예제 #4
0
        public BlackCalculator(StrikedTypePayoff payoff, double forward, double stdDev, double discount)
        {
            strike_   = payoff.strike();
            forward_  = forward;
            stdDev_   = stdDev;
            discount_ = discount;
            variance_ = stdDev * stdDev;

            Utils.QL_REQUIRE(forward > 0.0, () => "positive forward value required: " + forward + " not allowed");
            Utils.QL_REQUIRE(stdDev >= 0.0, () => "non-negative standard deviation required: " + stdDev + " not allowed");
            Utils.QL_REQUIRE(discount > 0.0, () => "positive discount required: " + discount + " not allowed");

            if (stdDev_ >= Const.QL_EPSILON)
            {
                if (strike_.IsEqual(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:
                Utils.QL_FAIL("invalid option type");
                break;
            }

            // now dispatch on type.

            Calculator calc = new Calculator(this);

            payoff.accept(calc);
        }
예제 #5
0
        // critical commodity price
        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 = 0;

            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:
                Utils.QL_FAIL("unknown option type");
                break;
            }


            // 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 = (!Utils.close(riskFreeDiscount, 1.0, 1000))
                    ? -2.0 * Math.Log(riskFreeDiscount)
                       / (variance * (1.0 - riskFreeDiscount))
                    : 2.0 / variance;

            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:
                Utils.QL_FAIL("unknown option type");
                break;
            }

            return(Si);
        }
        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_.IsEqual(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;
            }
        }