Ejemplo n.º 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_ = ......;
            }

        }
Ejemplo n.º 2
0
        // 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;
        }
        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;
                }
            }
        }
Ejemplo n.º 4
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);
        }
Ejemplo n.º 5
0
 void REPORT_FAILURE(string greekName, StrikedTypePayoff payoff, Exercise exercise, double s, double q, double r,
     Date today, double v, double expected, double calculated, double error, double tolerance)
 {
     Assert.Fail(exercise + " "
         + payoff.optionType() + " option with "
         + payoff + " payoff:\n"
         + "    spot value:       " + s + "\n"
         + "    strike:           " + payoff.strike() + "\n"
         + "    dividend yield:   " + q + "\n"
         + "    risk-free rate:   " + r + "\n"
         + "    reference date:   " + today + "\n"
         + "    maturity:         " + exercise.lastDate() + "\n"
         + "    volatility:       " + v + "\n\n"
         + "    expected " + greekName + ":   " + expected + "\n"
         + "    calculated " + greekName + ": " + calculated + "\n"
         + "    error:            " + error + "\n"
         + "    tolerance:        " + tolerance);
 }
Ejemplo n.º 6
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);
        }
Ejemplo n.º 7
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

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

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

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

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

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

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

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

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

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

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

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

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

                default:
                    Utils.QL_FAIL("unknown option type");
                    break;
                }
            } // end of "early exercise can be optimal"
        }
Ejemplo n.º 8
0
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.American, () => "not an American Option");

            AmericanExercise ex = arguments_.exercise as AmericanExercise;

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

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

            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

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

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

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

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

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

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

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

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

                results_.strikeSensitivity  = black.strikeSensitivity();
                results_.itmCashProbability = black.itmCashProbability();
            }
            else
            {
                // early exercise can be optimal
                CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
                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"
        }