public AnalyticDoubleBarrierBinaryEngineHelper(
     GeneralizedBlackScholesProcess process,
     CashOrNothingPayoff payoff,
     DoubleBarrierOption.Arguments arguments)
 {
     process_   = process;
     payoff_    = payoff;
     arguments_ = arguments;
 }
示例#2
0
            public void visit(CashOrNothingPayoff payoff)
            {
                black_.alpha_     = black_.DalphaDd1_ = 0.0;
                black_.X_         = payoff.cashPayoff();
                black_.DXDstrike_ = 0.0;
                switch (payoff.optionType())
                {
                case Option.Type.Call:
                    black_.beta_     = black_.cum_d2_;
                    black_.DbetaDd2_ = black_.n_d2_;
                    break;

                case Option.Type.Put:
                    black_.beta_     = 1.0 - black_.cum_d2_;
                    black_.DbetaDd2_ = -black_.n_d2_;
                    break;

                default:
                    Utils.QL_FAIL("invalid option type");
                    break;
                }
            }
示例#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 override void calculate()
        {
            // 1. Mesher
            HestonProcess process  = model_.currentLink().process();
            double        maturity = process.time(arguments_.exercise.lastDate());

            // 1.1 The variance mesher
            int tGridMin      = 5;
            int tGridAvgSteps = Math.Max(tGridMin, tGrid_ / 50);
            FdmHestonLocalVolatilityVarianceMesher varianceMesher
                = new FdmHestonLocalVolatilityVarianceMesher(vGrid_, process, leverageFct_, maturity, tGridAvgSteps);

            // 1.2 the equity mesher
            StrikedTypePayoff payoff = arguments_.payoff as StrikedTypePayoff;

            double?xMin = null;
            double?xMax = null;

            if (arguments_.barrierType == Barrier.Type.DownIn ||
                arguments_.barrierType == Barrier.Type.DownOut)
            {
                xMin = Math.Log(arguments_.barrier.Value);
            }
            if (arguments_.barrierType == Barrier.Type.UpIn ||
                arguments_.barrierType == Barrier.Type.UpOut)
            {
                xMax = Math.Log(arguments_.barrier.Value);
            }

            Fdm1dMesher equityMesher =
                new FdmBlackScholesMesher(xGrid_,
                                          FdmBlackScholesMesher.processHelper(process.s0(),
                                                                              process.dividendYield(),
                                                                              process.riskFreeRate(),
                                                                              varianceMesher.volaEstimate()),
                                          maturity,
                                          payoff.strike(),
                                          xMin,
                                          xMax,
                                          0.0001,
                                          1.5,
                                          new Pair <double?, double?>(),
                                          arguments_.cashFlow);

            FdmMesher mesher =
                new FdmMesherComposite(equityMesher, varianceMesher);

            // 2. Calculator
            StrikedTypePayoff rebatePayoff =
                new CashOrNothingPayoff(Option.Type.Call, 0.0, arguments_.rebate.Value);
            FdmInnerValueCalculator calculator =
                new FdmLogInnerValue(rebatePayoff, mesher, 0);

            // 3. Step conditions
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European,
                             () => "only european style option are supported");

            FdmStepConditionComposite conditions =
                FdmStepConditionComposite.vanillaComposite(
                    arguments_.cashFlow, arguments_.exercise,
                    mesher, calculator,
                    process.riskFreeRate().currentLink().referenceDate(),
                    process.riskFreeRate().currentLink().dayCounter());

            // 4. Boundary conditions
            FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet();

            if (arguments_.barrierType == Barrier.Type.DownIn ||
                arguments_.barrierType == Barrier.Type.DownOut)
            {
                boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0,
                                                        FdmDirichletBoundary.Side.Lower));
            }
            if (arguments_.barrierType == Barrier.Type.UpIn ||
                arguments_.barrierType == Barrier.Type.UpOut)
            {
                boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0,
                                                        FdmDirichletBoundary.Side.Upper));
            }

            // 5. Solver
            FdmSolverDesc solverDesc = new FdmSolverDesc();

            solverDesc.mesher       = mesher;
            solverDesc.bcSet        = boundaries;
            solverDesc.condition    = conditions;
            solverDesc.calculator   = calculator;
            solverDesc.maturity     = maturity;
            solverDesc.dampingSteps = dampingSteps_;
            solverDesc.timeSteps    = tGrid_;

            FdmHestonSolver solver =
                new FdmHestonSolver(
                    new Handle <HestonProcess>(process),
                    solverDesc, schemeDesc_,
                    new Handle <FdmQuantoHelper>(),
                    leverageFct_);

            double spot = process.s0().currentLink().value();

            results_.value = solver.valueAt(spot, process.v0());
            results_.delta = solver.deltaAt(spot, process.v0());
            results_.gamma = solver.gammaAt(spot, process.v0());
            results_.theta = solver.thetaAt(spot, process.v0());
        }
        public override void calculate()
        {
            if (arguments_.barrierType == DoubleBarrier.Type.KIKO ||
                arguments_.barrierType == DoubleBarrier.Type.KOKI)
            {
                AmericanExercise ex = arguments_.exercise as AmericanExercise;
                Utils.QL_REQUIRE(ex != null, () => "KIKO/KOKI options must have American exercise");
                Utils.QL_REQUIRE(ex.dates()[0] <=
                                 process_.blackVolatility().currentLink().referenceDate(),
                                 () => "American option with window exercise not handled yet");
            }
            else
            {
                EuropeanExercise ex = arguments_.exercise as EuropeanExercise;
                Utils.QL_REQUIRE(ex != null, () => "non-European exercise given");
            }
            CashOrNothingPayoff payoff = arguments_.payoff as CashOrNothingPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "a cash-or-nothing payoff must be given");

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

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

            double variance =
                process_.blackVolatility().currentLink().blackVariance(
                    arguments_.exercise.lastDate(),
                    payoff.strike());
            double barrier_lo = arguments_.barrier_lo.Value;
            double barrier_hi = arguments_.barrier_hi.Value;

            DoubleBarrier.Type barrierType = arguments_.barrierType;
            Utils.QL_REQUIRE(barrier_lo > 0.0,
                             () => "positive low barrier value required");
            Utils.QL_REQUIRE(barrier_hi > 0.0,
                             () => "positive high barrier value required");
            Utils.QL_REQUIRE(barrier_lo < barrier_hi,
                             () => "barrier_lo must be < barrier_hi");
            Utils.QL_REQUIRE(barrierType == DoubleBarrier.Type.KnockIn ||
                             barrierType == DoubleBarrier.Type.KnockOut ||
                             barrierType == DoubleBarrier.Type.KIKO ||
                             barrierType == DoubleBarrier.Type.KOKI,
                             () => "Unsupported barrier type");

            // degenerate cases
            switch (barrierType)
            {
            case DoubleBarrier.Type.KnockOut:
                if (spot <= barrier_lo || spot >= barrier_hi)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KnockIn:
                if (spot <= barrier_lo || spot >= barrier_hi)
                {
                    // knocked in - pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KIKO:
                if (spot >= barrier_hi)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                else if (spot <= barrier_lo)
                {
                    // knocked in, pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;

            case DoubleBarrier.Type.KOKI:
                if (spot <= barrier_lo)
                {
                    // knocked out, no value
                    results_.value = 0;
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                else if (spot >= barrier_hi)
                {
                    // knocked in, pays
                    results_.value = payoff.cashPayoff();
                    results_.delta = 0;
                    results_.gamma = 0;
                    results_.vega  = 0;
                    results_.rho   = 0;
                    return;
                }
                break;
            }

            AnalyticDoubleBarrierBinaryEngineHelper helper = new AnalyticDoubleBarrierBinaryEngineHelper(process_,
                                                                                                         payoff, arguments_);

            switch (barrierType)
            {
            case DoubleBarrier.Type.KnockOut:
            case DoubleBarrier.Type.KnockIn:
                results_.value = helper.payoffAtExpiry(spot, variance, barrierType);
                break;

            case DoubleBarrier.Type.KIKO:
            case DoubleBarrier.Type.KOKI:
                results_.value = helper.payoffKIKO(spot, variance, barrierType);
                break;

            default:
                results_.value = null;
                break;
            }
        }
        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);
        }
示例#7
0
        public override void calculate()
        {
            // 1. Mesher
            StrikedTypePayoff payoff   = arguments_.payoff as StrikedTypePayoff;
            double            maturity = process_.time(arguments_.exercise.lastDate());

            double?xMin = null;
            double?xMax = null;

            if (arguments_.barrierType == Barrier.Type.DownIn ||
                arguments_.barrierType == Barrier.Type.DownOut)
            {
                xMin = Math.Log(arguments_.barrier.Value);
            }
            if (arguments_.barrierType == Barrier.Type.UpIn ||
                arguments_.barrierType == Barrier.Type.UpOut)
            {
                xMax = Math.Log(arguments_.barrier.Value);
            }

            Fdm1dMesher equityMesher =
                new FdmBlackScholesMesher(xGrid_, process_, maturity,
                                          payoff.strike(), xMin, xMax, 0.0001, 1.5,
                                          new Pair <double?, double?>(),
                                          arguments_.cashFlow);

            FdmMesher mesher =
                new FdmMesherComposite(equityMesher);

            // 2. Calculator
            StrikedTypePayoff rebatePayoff =
                new CashOrNothingPayoff(Option.Type.Call, 0.0, arguments_.rebate.Value);
            FdmInnerValueCalculator calculator =
                new FdmLogInnerValue(rebatePayoff, mesher, 0);

            // 3. Step conditions
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European,
                             () => "only european style option are supported");

            FdmStepConditionComposite conditions =
                FdmStepConditionComposite.vanillaComposite(
                    arguments_.cashFlow, arguments_.exercise,
                    mesher, calculator,
                    process_.riskFreeRate().currentLink().referenceDate(),
                    process_.riskFreeRate().currentLink().dayCounter());

            // 4. Boundary conditions
            FdmBoundaryConditionSet boundaries = new FdmBoundaryConditionSet();

            if (arguments_.barrierType == Barrier.Type.DownIn ||
                arguments_.barrierType == Barrier.Type.DownOut)
            {
                boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0,
                                                        FdmDirichletBoundary.Side.Lower));
            }
            if (arguments_.barrierType == Barrier.Type.UpIn ||
                arguments_.barrierType == Barrier.Type.UpOut)
            {
                boundaries.Add(new FdmDirichletBoundary(mesher, arguments_.rebate.Value, 0,
                                                        FdmDirichletBoundary.Side.Upper));
            }

            // 5. Solver
            FdmSolverDesc solverDesc = new FdmSolverDesc();

            solverDesc.mesher       = mesher;
            solverDesc.bcSet        = boundaries;
            solverDesc.condition    = conditions;
            solverDesc.calculator   = calculator;
            solverDesc.maturity     = maturity;
            solverDesc.dampingSteps = dampingSteps_;
            solverDesc.timeSteps    = tGrid_;

            FdmBlackScholesSolver solver =
                new FdmBlackScholesSolver(
                    new Handle <GeneralizedBlackScholesProcess>(process_),
                    payoff.strike(), solverDesc, schemeDesc_,
                    localVol_, illegalLocalVolOverwrite_);

            double spot = process_.x0();

            results_.value = solver.valueAt(spot);
            results_.delta = solver.deltaAt(spot);
            results_.gamma = solver.gammaAt(spot);
            results_.theta = solver.thetaAt(spot);
        }
        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;
            }
        }