Esempio n. 1
0
        // Instrument interface
        public override void calculate()
        {
            Utils.QL_REQUIRE(!discountCurve_.empty(), () => "discounting term structure handle is empty");

            results_.value         = results_.cash = 0;
            results_.errorEstimate = null;

            Date refDate = discountCurve_.link.referenceDate();

            Date settlementDate = settlementDate_;

            if (settlementDate_ == null)
            {
                settlementDate = refDate;
            }
            else
            {
                Utils.QL_REQUIRE(settlementDate >= refDate, () =>
                                 "settlement date (" + settlementDate + ") before " +
                                 "discount curve reference date (" + refDate + ")");
            }

            results_.valuationDate = npvDate_;
            if (npvDate_ == null)
            {
                results_.valuationDate = refDate;
            }
            else
            {
                Utils.QL_REQUIRE(npvDate_ >= refDate, () =>
                                 "npv date (" + npvDate_ + ") before " +
                                 "discount curve reference date (" + refDate + ")");
            }

            results_.npvDateDiscount = discountCurve_.link.discount(results_.valuationDate);

            int n = arguments_.legs.Count;

            results_.legNPV         = new InitializedList <double?>(n);
            results_.legBPS         = new InitializedList <double?>(n);
            results_.startDiscounts = new InitializedList <double?>(n);
            results_.endDiscounts   = new InitializedList <double?>(n);

            bool includeRefDateFlows =
                includeSettlementDateFlows_.HasValue ?
                includeSettlementDateFlows_.Value :
                Settings.includeReferenceDateEvents;

            for (int i = 0; i < n; ++i)
            {
                try
                {
                    YieldTermStructure discount_ref = discountCurve_.currentLink();
                    double             npv = 0, bps = 0;
                    CashFlows.npvbps(arguments_.legs[i],
                                     discount_ref,
                                     includeRefDateFlows,
                                     settlementDate,
                                     results_.valuationDate,
                                     out npv,
                                     out bps);
                    results_.legNPV[i] = npv * arguments_.payer[i];
                    results_.legBPS[i] = bps * arguments_.payer[i];

                    Date d1 = CashFlows.startDate(arguments_.legs[i]);
                    if (d1 >= refDate)
                    {
                        results_.startDiscounts[i] = discountCurve_.link.discount(d1);
                    }
                    else
                    {
                        results_.startDiscounts[i] = null;
                    }

                    Date d2 = CashFlows.maturityDate(arguments_.legs[i]);
                    if (d2 >= refDate)
                    {
                        results_.endDiscounts[i] = discountCurve_.link.discount(d2);
                    }
                    else
                    {
                        results_.endDiscounts[i] = null;
                    }
                }
                catch (Exception e)
                {
                    Utils.QL_FAIL((i + 1) + " leg: " + e.Message);
                }
                results_.value += results_.legNPV[i];

                //results_.legNPV[i] = arguments_.payer[i] * CashFlows.npv(arguments_.legs[i], discountCurve_);
                //results_.legBPS[i] = arguments_.payer[i] * CashFlows.bps(arguments_.legs[i], discountCurve_);
                //results_.value += results_.legNPV[i];
                //results_.cash += arguments_.payer[i] * CashFlows.cash(arguments_.legs[i]);
                //try
                //{
                //   Date d = CashFlows.startDate(arguments_.legs[i]);
                //   startDiscounts[i] = discountCurve_.link.discount(d);
                //}
                //catch
                //{
                //   startDiscounts[i] = null;
                //}
            }
            //results_.additionalResults.Add("startDiscounts", startDiscounts);
        }
Esempio n. 2
0
        /*! This method returns the zero of the function \f$ f \f$, determined with the given accuracy \f$ \epsilon \f$
         *          depending on the particular solver, this might mean that the returned \f$ x \f$ is such that \f$ |f(x)| < \epsilon
         *          \f$, or that \f$ |x-\xi| < \epsilon \f$ where \f$ \xi \f$ is the real zero.
         *
         *          This method contains a bracketing routine to which an initial guess must be supplied as well as a step used to
         *          scan the range of the possible bracketing values.
         */

        public double solve(ISolver1d f, double accuracy, double guess, double step)
        {
            Utils.QL_REQUIRE(accuracy > 0.0, () => "accuracy (" + accuracy + ") must be positive");

            // check whether we really want to use epsilon
            accuracy = Math.Max(accuracy, Const.QL_EPSILON);

            const double growthFactor = 1.6;
            int          flipflop     = -1;

            root_  = guess;
            fxMax_ = f.value(root_);

            // monotonically crescent bias, as in optionValue(volatility)
            if (Utils.close(fxMax_, 0.0))
            {
                return(root_);
            }
            else if (fxMax_ > 0.0)
            {
                xMin_  = enforceBounds_(root_ - step);
                fxMin_ = f.value(xMin_);
                xMax_  = root_;
            }
            else
            {
                xMin_  = root_;
                fxMin_ = fxMax_;
                xMax_  = enforceBounds_(root_ + step);
                fxMax_ = f.value(xMax_);
            }

            evaluationNumber_ = 2;
            while (evaluationNumber_ <= maxEvaluations_)
            {
                if (fxMin_ * fxMax_ <= 0.0)
                {
                    if (Utils.close(fxMin_, 0.0))
                    {
                        return(xMin_);
                    }
                    if (Utils.close(fxMax_, 0.0))
                    {
                        return(xMax_);
                    }
                    root_ = (xMax_ + xMin_) / 2.0;
                    return(solveImpl(f, accuracy));
                }
                if (Math.Abs(fxMin_) < Math.Abs(fxMax_))
                {
                    xMin_  = enforceBounds_(xMin_ + growthFactor * (xMin_ - xMax_));
                    fxMin_ = f.value(xMin_);
                }
                else if (Math.Abs(fxMin_) > Math.Abs(fxMax_))
                {
                    xMax_  = enforceBounds_(xMax_ + growthFactor * (xMax_ - xMin_));
                    fxMax_ = f.value(xMax_);
                }
                else if (flipflop == -1)
                {
                    xMin_  = enforceBounds_(xMin_ + growthFactor * (xMin_ - xMax_));
                    fxMin_ = f.value(xMin_);
                    evaluationNumber_++;
                    flipflop = 1;
                }
                else if (flipflop == 1)
                {
                    xMax_    = enforceBounds_(xMax_ + growthFactor * (xMax_ - xMin_));
                    fxMax_   = f.value(xMax_);
                    flipflop = -1;
                }
                evaluationNumber_++;
            }

            Utils.QL_FAIL("unable to bracket root in " + maxEvaluations_
                          + " function evaluations (last bracket attempt: " + "f[" + xMin_ + "," + xMax_ +
                          "] "
                          + "-> [" + fxMin_ + "," + fxMax_ + "])");
            return(0);
        }
Esempio n. 3
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);
        }
Esempio n. 4
0
        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;
            }
        }
Esempio n. 5
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"
        }
Esempio n. 6
0
 //! usually only need implement this (of course they may need
 //! to re-implement initialize too ...)
 protected virtual double optionletPriceImp(Option.Type optionType, double strike, double forward, double stdDev)
 {
     Utils.QL_FAIL("you must implement this to get a vol-dependent price");
     return(strike * forward * stdDev * (int)optionType);
 }
Esempio n. 7
0
        public override void calculate()
        {
            double basisPoint = 1.0e-4;

            Date exerciseDate = arguments_.exercise.date(0);

            // the part of the swap preceding exerciseDate should be truncated
            // to avoid taking into account unwanted cashflows
            VanillaSwap swap = arguments_.swap;

            double strike = swap.fixedRate;

            // using the forecasting curve
            swap.setPricingEngine(new DiscountingSwapEngine(swap.iborIndex().forwardingTermStructure()));
            double atmForward = swap.fairRate();

            // Volatilities are quoted for zero-spreaded swaps.
            // Therefore, any spread on the floating leg must be removed
            // with a corresponding correction on the fixed leg.
            if (swap.spread.IsNotEqual(0.0))
            {
                double correction = swap.spread * Math.Abs(swap.floatingLegBPS() / swap.fixedLegBPS());
                strike     -= correction;
                atmForward -= correction;
                results_.additionalResults["spreadCorrection"] = correction;
            }
            else
            {
                results_.additionalResults["spreadCorrection"] = 0.0;
            }
            results_.additionalResults["strike"]     = strike;
            results_.additionalResults["atmForward"] = atmForward;

            // using the discounting curve
            swap.setPricingEngine(new DiscountingSwapEngine(termStructure_));
            double annuity = 0;

            switch (arguments_.settlementType)
            {
            case Settlement.Type.Physical: {
                annuity = Math.Abs(swap.fixedLegBPS()) / basisPoint;
                break;
            }

            case Settlement.Type.Cash: {
                List <CashFlow> fixedLeg        = swap.fixedLeg();
                FixedRateCoupon firstCoupon     = (FixedRateCoupon)fixedLeg[0];
                DayCounter      dayCount        = firstCoupon.dayCounter();
                double          fixedLegCashBPS =
                    CashFlows.bps(fixedLeg,
                                  new InterestRate(atmForward, dayCount, QLNet.Compounding.Compounded, Frequency.Annual), false,
                                  termStructure_.link.referenceDate());
                annuity = Math.Abs(fixedLegCashBPS / basisPoint);
                break;
            }

            default:
                Utils.QL_FAIL("unknown settlement type");
                break;
            }
            results_.additionalResults["annuity"] = annuity;

            // the swap length calculation might be improved using the value date
            // of the exercise date
            double swapLength = volatility_.link.swapLength(exerciseDate,
                                                            arguments_.floatingPayDates.Last());

            results_.additionalResults["swapLength"] = swapLength;

            double variance = volatility_.link.blackVariance(exerciseDate,
                                                             swapLength,
                                                             strike);
            double stdDev = Math.Sqrt(variance);

            results_.additionalResults["stdDev"] = stdDev;
            Option.Type w = (arguments_.type == VanillaSwap.Type.Payer) ?
                            Option.Type.Call : Option.Type.Put;
            results_.value = Utils.blackFormula(w, strike, atmForward, stdDev, annuity);

            double exerciseTime = volatility_.link.timeFromReference(exerciseDate);

            results_.additionalResults["vega"] = Math.Sqrt(exerciseTime) *
                                                 Utils.blackFormulaStdDevDerivative(strike, atmForward, stdDev, annuity);
        }
Esempio n. 8
0
 /// <summary>
 /// not applicable here
 /// </summary>
 public override double capletPrice(double d)
 {
     Utils.QL_FAIL("not available");
     return(0);
 }
Esempio n. 9
0
 /// <summary>
 /// not applicable here
 /// </summary>
 public override double floorletRate(double d)
 {
     Utils.QL_FAIL("not available");
     return(0);
 }
Esempio n. 10
0
        // alternative delta type
        private double strikeFromDelta(double delta, DeltaVolQuote.DeltaType dt)
        {
            double res = 0.0;
            double arg = 0.0;
            InverseCumulativeNormal f = new InverseCumulativeNormal();

            Utils.QL_REQUIRE(delta * phi_ >= 0.0, () => "Option type and delta are incoherent.");

            switch (dt)
            {
            case DeltaVolQuote.DeltaType.Spot:
                Utils.QL_REQUIRE(Math.Abs(delta) <= fDiscount_, () => "Spot delta out of range.");
                arg = -phi_ *f.value(phi_ *delta / fDiscount_) * stdDev_ + 0.5 * stdDev_ * stdDev_;

                res = forward_ * Math.Exp(arg);
                break;

            case DeltaVolQuote.DeltaType.Fwd:
                Utils.QL_REQUIRE(Math.Abs(delta) <= 1.0, () => "Forward delta out of range.");
                arg = -phi_ *f.value(phi_ *delta) * stdDev_ + 0.5 * stdDev_ * stdDev_;

                res = forward_ * Math.Exp(arg);
                break;

            case DeltaVolQuote.DeltaType.PaSpot:
            case DeltaVolQuote.DeltaType.PaFwd:
                // This has to be solved numerically. One of the
                // problems is that the premium adjusted call delta is
                // not monotonic in strike, such that two solutions
                // might occur. The one right to the max of the delta is
                // considered to be the correct strike.  Some proper
                // interval bounds for the strike need to be chosen, the
                // numerics can otherwise be very unreliable and
                // unstable.  I've chosen Brent over Newton, since the
                // interval can be specified explicitly and we can not
                // run into the area on the left of the maximum.  The
                // put delta doesn't have this property and can be
                // solved without any problems, but also numerically.

                BlackDeltaPremiumAdjustedSolverClass f1 = new BlackDeltaPremiumAdjustedSolverClass(
                    ot_, dt, spot_, dDiscount_, fDiscount_, stdDev_, delta);

                Brent solver = new Brent();
                solver.setMaxEvaluations(1000);
                double accuracy = 1.0e-10;

                double rightLimit = 0.0;
                double leftLimit  = 0.0;

                // Strike of not premium adjusted is always to the right of premium adjusted
                if (dt == DeltaVolQuote.DeltaType.PaSpot)
                {
                    rightLimit = strikeFromDelta(delta, DeltaVolQuote.DeltaType.Spot);
                }
                else
                {
                    rightLimit = strikeFromDelta(delta, DeltaVolQuote.DeltaType.Fwd);
                }

                if (phi_ < 0)
                {
                    // if put
                    res = solver.solve(f1, accuracy, rightLimit, 0.0, spot_ * 100.0);
                    break;
                }
                else
                {
                    // find out the left limit which is the strike
                    // corresponding to the value where premium adjusted
                    // deltas have their maximum.

                    BlackDeltaPremiumAdjustedMaxStrikeClass g = new BlackDeltaPremiumAdjustedMaxStrikeClass(
                        ot_, dt, spot_, dDiscount_, fDiscount_, stdDev_);

                    leftLimit = solver.solve(g, accuracy, rightLimit * 0.5, 0.0, rightLimit);

                    double guess = leftLimit + (rightLimit - leftLimit) * 0.5;

                    res = solver.solve(f1, accuracy, guess, leftLimit, rightLimit);
                } // end if phi<0 else

                break;


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

            return(res);
        }
Esempio n. 11
0
 /// <summary>
 /// not applicable here
 /// </summary>
 public override double convexityAdjustment()
 {
     Utils.QL_FAIL("not defined for average-BMA coupon");
     return(0);
 }
Esempio n. 12
0
        protected void calculateNextGeneration(List <Candidate> population,
                                               CostFunction costFunction)
        {
            List <Candidate> mirrorPopulation = null;
            List <Candidate> oldPopulation    = (List <Candidate>)population.Clone();

            switch (configuration().strategy)
            {
            case Strategy.Rand1Standard:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                List <Candidate> shuffledPop2 = (List <Candidate>)population.Clone();
                population.Shuffle();
                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();

                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    population[popIter].values = population[popIter].values
                                                 + configuration().stepsizeWeight
                                                 *(shuffledPop1[popIter].values - shuffledPop2[popIter].values);
                }
            }
            break;

            case Strategy.BestMemberWithJitter:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                Vector jitter = new Vector(population[0].values.size(), 0.0);

                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    for (int jitterIter = 0; jitterIter < jitter.Count; jitterIter++)
                    {
                        jitter[jitterIter] = rng_.nextReal();
                    }

                    population[popIter].values = bestMemberEver_.values
                                                 + Vector.DirectMultiply(
                        shuffledPop1[popIter].values - population[popIter].values
                        , 0.0001 * jitter + configuration().stepsizeWeight);
                }

                mirrorPopulation = new InitializedList <Candidate>(population.Count);
                mirrorPopulation.ForEach((ii, vv) => mirrorPopulation[ii] = (Candidate)bestMemberEver_.Clone());
            }
            break;

            case Strategy.CurrentToBest2Diffs:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();

                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    population[popIter].values = oldPopulation[popIter].values
                                                 + configuration().stepsizeWeight
                                                 *(bestMemberEver_.values - oldPopulation[popIter].values)
                                                 + configuration().stepsizeWeight
                                                 *(population[popIter].values - shuffledPop1[popIter].values);
                }

                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();
            }
            break;

            case Strategy.Rand1DiffWithPerVectorDither:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                List <Candidate> shuffledPop2 = (List <Candidate>)population.Clone();
                population.Shuffle();
                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();
                Vector FWeight = new Vector(population.First().values.size(), 0.0);
                for (int fwIter = 0; fwIter < FWeight.Count; fwIter++)
                {
                    FWeight[fwIter] = (1.0 - configuration().stepsizeWeight)
                                      * rng_.nextReal() + configuration().stepsizeWeight;
                }
                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    population[popIter].values = population[popIter].values
                                                 + Vector.DirectMultiply(FWeight,
                                                                         shuffledPop1[popIter].values - shuffledPop2[popIter].values);
                }
            }
            break;

            case Strategy.Rand1DiffWithDither:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                List <Candidate> shuffledPop2 = (List <Candidate>)population.Clone();
                population.Shuffle();
                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();
                double FWeight = (1.0 - configuration().stepsizeWeight) * rng_.nextReal()
                                 + configuration().stepsizeWeight;
                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    population[popIter].values = population[popIter].values
                                                 + FWeight * (shuffledPop1[popIter].values -
                                                              shuffledPop2[popIter].values);
                }
            }
            break;

            case Strategy.EitherOrWithOptimalRecombination:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                List <Candidate> shuffledPop2 = (List <Candidate>)population.Clone();
                population.Shuffle();
                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();
                double probFWeight = 0.5;
                if (rng_.nextReal() < probFWeight)
                {
                    for (int popIter = 0; popIter < population.Count; popIter++)
                    {
                        population[popIter].values = oldPopulation[popIter].values
                                                     + configuration().stepsizeWeight
                                                     *(shuffledPop1[popIter].values - shuffledPop2[popIter].values);
                    }
                }
                else
                {
                    double K = 0.5 * (configuration().stepsizeWeight + 1); // invariant with respect to probFWeight used
                    for (int popIter = 0; popIter < population.Count; popIter++)
                    {
                        population[popIter].values = oldPopulation[popIter].values
                                                     + K
                                                     * (shuffledPop1[popIter].values - shuffledPop2[popIter].values
                                                        - 2.0 * population[popIter].values);
                    }
                }
            }
            break;

            case Strategy.Rand1SelfadaptiveWithRotation:
            {
                population.Shuffle();
                List <Candidate> shuffledPop1 = (List <Candidate>)population.Clone();
                population.Shuffle();
                List <Candidate> shuffledPop2 = (List <Candidate>)population.Clone();
                population.Shuffle();
                mirrorPopulation = (List <Candidate>)shuffledPop1.Clone();

                adaptSizeWeights();

                for (int popIter = 0; popIter < population.Count; popIter++)
                {
                    if (rng_.nextReal() < 0.1)
                    {
                        population[popIter].values = rotateArray(bestMemberEver_.values);
                    }
                    else
                    {
                        population[popIter].values = bestMemberEver_.values
                                                     + currGenSizeWeights_[popIter]
                                                     * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
                    }
                }
            }
            break;

            default:
                Utils.QL_FAIL("Unknown strategy ("
                              + Convert.ToInt32(configuration().strategy) + ")");
                break;
            }

            // in order to avoid unnecessary copying we use the same population object for mutants
            crossover(oldPopulation, population, population, mirrorPopulation,
                      costFunction);
        }
Esempio n. 13
0
        /* Generally inflation indices are available with a lag of 1month
         * and then observed with a lag of 2-3 months depending whether
         * they use an interpolated fixing or not.  Here, we make the
         * swap use the interpolation of the index to avoid incompatibilities.
         */
        public ZeroCouponInflationSwap(Type type,
                                       double nominal,
                                       Date startDate, // start date of contract (only)
                                       Date maturity,  // this is pre-adjustment!
                                       Calendar fixCalendar,
                                       BusinessDayConvention fixConvention,
                                       DayCounter dayCounter,
                                       double fixedRate,
                                       ZeroInflationIndex infIndex,
                                       Period observationLag,
                                       bool adjustInfObsDates = false,
                                       Calendar infCalendar   = null,
                                       BusinessDayConvention?infConvention = null)
            : base(2)
        {
            type_              = type;
            nominal_           = nominal;
            startDate_         = startDate;
            maturityDate_      = maturity;
            fixCalendar_       = fixCalendar;
            fixConvention_     = fixConvention;
            fixedRate_         = fixedRate;
            infIndex_          = infIndex;
            observationLag_    = observationLag;
            adjustInfObsDates_ = adjustInfObsDates;
            infCalendar_       = infCalendar;
            dayCounter_        = dayCounter;

            // first check compatibility of index and swap definitions
            if (infIndex_.interpolated())
            {
                Period pShift = new Period(infIndex_.frequency());
                Utils.QL_REQUIRE(observationLag_ - pShift > infIndex_.availabilityLag(), () =>
                                 "inconsistency between swap observation of index " + observationLag_ +
                                 " index availability " + infIndex_.availabilityLag() +
                                 " interpolated index period " + pShift +
                                 " and index availability " + infIndex_.availabilityLag() +
                                 " need (obsLag-index period) > availLag");
            }
            else
            {
                Utils.QL_REQUIRE(infIndex_.availabilityLag() < observationLag_, () =>
                                 "index tries to observe inflation fixings that do not yet exist: "
                                 + " availability lag " + infIndex_.availabilityLag()
                                 + " versus obs lag = " + observationLag_);
            }

            if (infCalendar_ == null)
            {
                infCalendar_ = fixCalendar_;
            }
            if (infConvention == null)
            {
                infConvention_ = fixConvention_;
            }
            else
            {
                infConvention_ = infConvention.Value;
            }

            if (adjustInfObsDates_)
            {
                baseDate_ = infCalendar_.adjust(startDate - observationLag_, infConvention_);
                obsDate_  = infCalendar_.adjust(maturity - observationLag_, infConvention_);
            }
            else
            {
                baseDate_ = startDate - observationLag_;
                obsDate_  = maturity - observationLag_;
            }

            Date infPayDate   = infCalendar_.adjust(maturity, infConvention_);
            Date fixedPayDate = fixCalendar_.adjust(maturity, fixConvention_);

            // At this point the index may not be able to forecast
            // i.e. do not want to force the existence of an inflation
            // term structure before allowing users to create instruments.
            double T = Utils.inflationYearFraction(infIndex_.frequency(), infIndex_.interpolated(),
                                                   dayCounter_, baseDate_, obsDate_);
            // N.B. the -1.0 is because swaps only exchange growth, not notionals as well
            double fixedAmount = nominal * (Math.Pow(1.0 + fixedRate, T) - 1.0);

            legs_[0].Add(new SimpleCashFlow(fixedAmount, fixedPayDate));
            bool growthOnly = true;

            legs_[1].Add(new IndexedCashFlow(nominal, infIndex, baseDate_, obsDate_, infPayDate, growthOnly));

            for (int j = 0; j < 2; ++j)
            {
                legs_[j].ForEach((i, x) => x.registerWith(update));
            }

            switch (type_)
            {
            case Type.Payer:
                payer_[0] = +1.0;
                payer_[1] = -1.0;
                break;

            case Type.Receiver:
                payer_[0] = -1.0;
                payer_[1] = +1.0;
                break;

            default:
                Utils.QL_FAIL("Unknown zero-inflation-swap type");
                break;
            }
        }
Esempio n. 14
0
        // function
        public double value(double a, double b)
        {
            CumulativeNormalDistribution cumNormalDist = new CumulativeNormalDistribution();
            double CumNormDistA     = cumNormalDist.value(a);
            double CumNormDistB     = cumNormalDist.value(b);
            double MaxCumNormDistAB = Math.Max(CumNormDistA, CumNormDistB);
            double MinCumNormDistAB = Math.Min(CumNormDistA, CumNormDistB);

            if (1.0 - MaxCumNormDistAB < 1e-15)
            {
                return(MinCumNormDistAB);
            }

            if (MinCumNormDistAB < 1e-15)
            {
                return(MinCumNormDistAB);
            }

            double a1 = a / Math.Sqrt(2.0 * (1.0 - rho2_));
            double b1 = b / Math.Sqrt(2.0 * (1.0 - rho2_));

            double result = -1.0;

            if (a <= 0.0 && b <= 0 && rho_ <= 0)
            {
                double sum = 0.0;
                for (int i = 0; i < 5; i++)
                {
                    for (int j = 0; j < 5; j++)
                    {
                        sum += x_[i] * x_[j] * Math.Exp(a1 * (2.0 * y_[i] - a1) + b1 * (2.0 * y_[j] - b1) + 2.0 * rho_ * (y_[i] - a1) * (y_[j] - b1));
                    }
                }
                result = Math.Sqrt(1.0 - rho2_) / Const.M_PI * sum;
            }
            else if (a <= 0 && b >= 0 && rho_ >= 0)
            {
                BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(-rho_);
                result = CumNormDistA - bivCumNormalDist.value(a, -b);
            }
            else if (a >= 0.0 && b <= 0.0 && rho_ >= 0.0)
            {
                BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(-rho_);
                result = CumNormDistB - bivCumNormalDist.value(-a, b);
            }
            else if (a >= 0.0 && b >= 0.0 && rho_ <= 0.0)
            {
                result = CumNormDistA + CumNormDistB - 1.0 + (this.value(-a, -b));
            }
            else if (a * b * rho_ > 0.0)
            {
                double rho1 = (rho_ * a - b) * (a > 0.0 ? 1.0 : -1.0) / Math.Sqrt(a * a - 2.0 * rho_ * a * b + b * b);
                BivariateCumulativeNormalDistributionDr78 bivCumNormalDist = new BivariateCumulativeNormalDistributionDr78(rho1);

                double rho2 = (rho_ * b - a) * (b > 0.0 ? 1.0 : -1.0) / Math.Sqrt(a * a - 2.0 * rho_ * a * b + b * b);
                BivariateCumulativeNormalDistributionDr78 CBND2 = new BivariateCumulativeNormalDistributionDr78(rho2);

                double delta = (1.0 - (a > 0.0 ? 1.0 : -1.0) * (b > 0.0 ? 1.0 : -1.0)) / 4.0;

                result = bivCumNormalDist.value(a, 0.0) + CBND2.value(b, 0.0) - delta;
            }
            else
            {
                Utils.QL_FAIL("case not handled");
            }

            return(result);
        }
Esempio n. 15
0
 public override double secondDerivative(double d)
 {
     Utils.QL_FAIL("Second derivative calculation not implemented for kernel interpolation");
     return(0);
 }
Esempio n. 16
0
 /// <summary>
 /// Get the fixing date
 /// </summary>
 /// <remarks>FloatingRateCoupon interface not applicable here; use <c>fixingDates()</c> instead
 /// </remarks>
 public override Date fixingDate()
 {
     Utils.QL_FAIL("no single fixing date for average-BMA coupon");
     return(null);
 }
Esempio n. 17
0
        public override void calculate()
        {
            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

            Utils.QL_REQUIRE(payoff != null, () => "non-plain payoff given");
            Utils.QL_REQUIRE(payoff.strike() > 0.0, () => "strike must be positive");

            double strike = payoff.strike();
            double spot   = process_.x0();

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

            Barrier.Type barrierType = arguments_.barrierType;

            switch (payoff.optionType())
            {
            case Option.Type.Call:
                switch (barrierType)
                {
                case Barrier.Type.DownIn:
                    if (strike >= barrier())
                    {
                        results_.value = C(1, 1) + E(1);
                    }
                    else
                    {
                        results_.value = A(1) - B(1) + D(1, 1) + E(1);
                    }
                    break;

                case Barrier.Type.UpIn:
                    if (strike >= barrier())
                    {
                        results_.value = A(1) + E(-1);
                    }
                    else
                    {
                        results_.value = B(1) - C(-1, 1) + D(-1, 1) + E(-1);
                    }
                    break;

                case Barrier.Type.DownOut:
                    if (strike >= barrier())
                    {
                        results_.value = A(1) - C(1, 1) + F(1);
                    }
                    else
                    {
                        results_.value = B(1) - D(1, 1) + F(1);
                    }
                    break;

                case Barrier.Type.UpOut:
                    if (strike >= barrier())
                    {
                        results_.value = F(-1);
                    }
                    else
                    {
                        results_.value = A(1) - B(1) + C(-1, 1) - D(-1, 1) + F(-1);
                    }
                    break;
                }
                break;

            case Option.Type.Put:
                switch (barrierType)
                {
                case Barrier.Type.DownIn:
                    if (strike >= barrier())
                    {
                        results_.value = B(-1) - C(1, -1) + D(1, -1) + E(1);
                    }
                    else
                    {
                        results_.value = A(-1) + E(1);
                    }
                    break;

                case Barrier.Type.UpIn:
                    if (strike >= barrier())
                    {
                        results_.value = A(-1) - B(-1) + D(-1, -1) + E(-1);
                    }
                    else
                    {
                        results_.value = C(-1, -1) + E(-1);
                    }
                    break;

                case Barrier.Type.DownOut:
                    if (strike >= barrier())
                    {
                        results_.value = A(-1) - B(-1) + C(1, -1) - D(1, -1) + F(1);
                    }
                    else
                    {
                        results_.value = F(1);
                    }
                    break;

                case Barrier.Type.UpOut:
                    if (strike >= barrier())
                    {
                        results_.value = B(-1) - D(-1, -1) + F(-1);
                    }
                    else
                    {
                        results_.value = A(-1) - C(-1, -1) + F(-1);
                    }
                    break;
                }
                break;

            default:
                Utils.QL_FAIL("unknown type");
                break;
            }
        }
Esempio n. 18
0
 /// <summary>
 /// not applicable here; use indexFixings() instead
 /// </summary>
 public override double indexFixing()
 {
     Utils.QL_FAIL("no single fixing for average-BMA coupon");
     return(0);
 }
Esempio n. 19
0
        public static Period operator+(Period p1, Period p2)
        {
            int      length_ = p1.length();
            TimeUnit units_  = p1.units();

            if (length_ == 0)
            {
                length_ = p2.length();
                units_  = p2.units();
            }
            else if (units_ == p2.units())
            {
                // no conversion needed
                length_ += p2.length();
            }
            else
            {
                switch (units_)
                {
                case TimeUnit.Years:
                    switch (p2.units())
                    {
                    case TimeUnit.Months:
                        units_  = TimeUnit.Months;
                        length_ = length_ * 12 + p2.length();
                        break;

                    case TimeUnit.Weeks:
                    case TimeUnit.Days:
                        Utils.QL_REQUIRE(p1.length() == 0, () => "impossible addition between " + p1 + " and " + p2);
                        break;

                    default:
                        Utils.QL_FAIL("unknown time unit (" + p2.units() + ")");
                        break;
                    }
                    break;

                case TimeUnit.Months:
                    switch (p2.units())
                    {
                    case TimeUnit.Years:
                        length_ += p2.length() * 12;
                        break;

                    case TimeUnit.Weeks:
                    case TimeUnit.Days:
                        Utils.QL_REQUIRE(p1.length() == 0, () => "impossible addition between " + p1 + " and " + p2);
                        break;

                    default:
                        Utils.QL_FAIL("unknown time unit (" + p2.units() + ")");
                        break;
                    }
                    break;

                case TimeUnit.Weeks:
                    switch (p2.units())
                    {
                    case TimeUnit.Days:
                        units_  = TimeUnit.Days;
                        length_ = length_ * 7 + p2.length();
                        break;

                    case TimeUnit.Years:
                    case TimeUnit.Months:
                        Utils.QL_REQUIRE(p1.length() == 0, () => "impossible addition between " + p1 + " and " + p2);
                        break;

                    default:
                        Utils.QL_FAIL("unknown time unit (" + p2.units() + ")");
                        break;
                    }
                    break;

                case TimeUnit.Days:
                    switch (p2.units())
                    {
                    case TimeUnit.Weeks:
                        length_ += p2.length() * 7;
                        break;

                    case TimeUnit.Years:
                    case TimeUnit.Months:
                        Utils.QL_REQUIRE(p1.length() == 0, () => "impossible addition between " + p1 + " and " + p2);
                        break;

                    default:
                        Utils.QL_FAIL("unknown time unit (" + p2.units() + ")");
                        break;
                    }
                    break;

                default:
                    Utils.QL_FAIL("unknown time unit (" + units_ + ")");
                    break;
                }
            }
            return(new Period(length_, units_));
        }
Esempio n. 20
0
        /// <summary>
        /// Adjusts a non-business day to the appropriate near business day  with respect
        /// to the given convention.
        /// </summary>
        public Date adjust(Date d, BusinessDayConvention c = BusinessDayConvention.Following)
        {
            if (d == null)
            {
                throw new ArgumentException("null date");
            }
            if (c == BusinessDayConvention.Unadjusted)
            {
                return(d);
            }

            Date d1 = d;

            if (c == BusinessDayConvention.Following || c == BusinessDayConvention.ModifiedFollowing ||
                c == BusinessDayConvention.HalfMonthModifiedFollowing)
            {
                while (isHoliday(d1))
                {
                    d1++;
                }
                if (c == BusinessDayConvention.ModifiedFollowing || c == BusinessDayConvention.HalfMonthModifiedFollowing)
                {
                    if (d1.Month != d.Month)
                    {
                        return(adjust(d, BusinessDayConvention.Preceding));
                    }
                    if (c == BusinessDayConvention.HalfMonthModifiedFollowing)
                    {
                        if (d.Day <= 15 && d1.Day > 15)
                        {
                            return(adjust(d, BusinessDayConvention.Preceding));
                        }
                    }
                }
            }
            else if (c == BusinessDayConvention.Preceding || c == BusinessDayConvention.ModifiedPreceding)
            {
                while (isHoliday(d1))
                {
                    d1--;
                }
                if (c == BusinessDayConvention.ModifiedPreceding && d1.Month != d.Month)
                {
                    return(adjust(d, BusinessDayConvention.Following));
                }
            }
            else if (c == BusinessDayConvention.Nearest)
            {
                Date d2 = d;
                while (isHoliday(d1) && isHoliday(d2))
                {
                    d1++;
                    d2--;
                }
                if (isHoliday(d1))
                {
                    return(d2);
                }
                else
                {
                    return(d1);
                }
            }
            else
            {
                Utils.QL_FAIL("unknown business-day convention");
            }
            return(d1);
        }
        public override void calculate()
        {
            Utils.QL_REQUIRE(arguments_.averageType == Average.Type.Geometric, () =>
                             "not a geometric average option");

            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () =>
                             "not an European option");

            Utils.QL_REQUIRE(arguments_.runningAccumulator > 0.0, () =>
                             "positive running product required: " + arguments_.runningAccumulator + "not allowed");

            double runningLog  = Math.Log(arguments_.runningAccumulator.GetValueOrDefault());
            int?   pastFixings = arguments_.pastFixings;

            Utils.QL_REQUIRE(pastFixings == 0, () => "past fixings currently not managed");

            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

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

            DayCounter rfdc  = process_.riskFreeRate().link.dayCounter();
            DayCounter divdc = process_.dividendYield().link.dayCounter();
            DayCounter voldc = process_.blackVolatility().link.dayCounter();

            List <double> fixingTimes = new List <double>();

            for (int i = 0; i < arguments_.fixingDates.Count; i++)
            {
                if (arguments_.fixingDates[i] >= arguments_.fixingDates[0])
                {
                    double t = voldc.yearFraction(arguments_.fixingDates[0], arguments_.fixingDates[i]);
                    fixingTimes.Add(t);
                }
            }

            int    remainingFixings = fixingTimes.Count;
            int    numberOfFixings  = pastFixings.GetValueOrDefault() + remainingFixings;
            double N = (double)(numberOfFixings);

            double pastWeight   = pastFixings.GetValueOrDefault() / N;
            double futureWeight = 1.0 - pastWeight;

            double timeSum = 0;

            fixingTimes.ForEach((ii, vv) => timeSum += fixingTimes[ii]);

            double residualTime = rfdc.yearFraction(arguments_.fixingDates[pastFixings.GetValueOrDefault()],
                                                    arguments_.exercise.lastDate());


            double underlying = process_.stateVariable().link.value();

            Utils.QL_REQUIRE(underlying > 0.0, () => "positive underlying value required");

            double volatility = process_.blackVolatility().link.blackVol(arguments_.exercise.lastDate(), underlying);

            Date   exDate       = arguments_.exercise.lastDate();
            double dividendRate = process_.dividendYield().link.zeroRate(exDate, divdc,
                                                                         Compounding.Continuous, Frequency.NoFrequency).value();

            double riskFreeRate = process_.riskFreeRate().link.zeroRate(exDate, rfdc,
                                                                        Compounding.Continuous, Frequency.NoFrequency).value();

            double nu = riskFreeRate - dividendRate - 0.5 * volatility * volatility;

            double temp = 0.0;

            for (int i = pastFixings.GetValueOrDefault() + 1; i < numberOfFixings; i++)
            {
                temp += fixingTimes[i - pastFixings.GetValueOrDefault() - 1] * (N - i);
            }
            double variance       = volatility * volatility / N / N * (timeSum + 2.0 * temp);
            double covarianceTerm = volatility * volatility / N * timeSum;
            double sigmaSum_2     = variance + volatility * volatility * residualTime - 2.0 * covarianceTerm;

            int    M = (pastFixings.GetValueOrDefault() == 0 ? 1 : pastFixings.GetValueOrDefault());
            double runningLogAverage = runningLog / M;

            double muG = pastWeight * runningLogAverage +
                         futureWeight * Math.Log(underlying) +
                         nu * timeSum / N;

            CumulativeNormalDistribution f = new CumulativeNormalDistribution();

            double y1 = (Math.Log(underlying) +
                         (riskFreeRate - dividendRate) * residualTime -
                         muG - variance / 2.0 + sigmaSum_2 / 2.0)
                        / Math.Sqrt(sigmaSum_2);
            double y2 = y1 - Math.Sqrt(sigmaSum_2);

            switch (payoff.optionType())
            {
            case Option.Type.Call:
                results_.value = underlying * Math.Exp(-dividendRate * residualTime)
                                 * f.value(y1) - Math.Exp(muG + variance / 2.0 - riskFreeRate * residualTime) * f.value(y2);
                break;

            case Option.Type.Put:
                results_.value = -underlying *Math.Exp(-dividendRate *residualTime)
                                 * f.value(-y1) + Math.Exp(muG + variance / 2.0 - riskFreeRate * residualTime) * f.value(-y2);

                break;

            default:
                Utils.QL_FAIL("invalid option type");
                break;
            }
        }
Esempio n. 22
0
        public override void calculate()
        {
            // this is an european option pricer
            Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "not an European option");

            // plain vanilla
            PlainVanillaPayoff payoff = arguments_.payoff as PlainVanillaPayoff;

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

            double v0        = model_.link.v0();
            double spotPrice = model_.link.s0();

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

            double strike = payoff.strike();
            double term   = model_.link.riskFreeRate().link.dayCounter().yearFraction(
                model_.link.riskFreeRate().link.referenceDate(), arguments_.exercise.lastDate());
            double riskFreeDiscount = model_.link.riskFreeRate().link.discount(arguments_.exercise.lastDate());
            double dividendDiscount = model_.link.dividendYield().link.discount(arguments_.exercise.lastDate());

            //average values
            TimeGrid timeGrid = model_.link.timeGrid();
            int      n = timeGrid.size() - 1;
            double   kappaAvg = 0.0, thetaAvg = 0.0, sigmaAvg = 0.0, rhoAvg = 0.0;

            for (int i = 1; i <= n; ++i)
            {
                double t = 0.5 * (timeGrid[i - 1] + timeGrid[i]);
                kappaAvg += model_.link.kappa(t);
                thetaAvg += model_.link.theta(t);
                sigmaAvg += model_.link.sigma(t);
                rhoAvg   += model_.link.rho(t);
            }
            kappaAvg /= n;
            thetaAvg /= n;
            sigmaAvg /= n;
            rhoAvg   /= n;

            double c_inf = Math.Min(10.0, Math.Max(0.0001,
                                                   Math.Sqrt(1.0 - Math.Pow(rhoAvg, 2)) / sigmaAvg)) * (v0 + kappaAvg * thetaAvg * term);

            double p1 = integration_.calculate(c_inf,
                                               new Fj_Helper(model_, term, strike, 1).value) / Const.M_PI;

            double p2 = integration_.calculate(c_inf,
                                               new Fj_Helper(model_, term, strike, 2).value) / Const.M_PI;

            switch (payoff.optionType())
            {
            case Option.Type.Call:
                results_.value = spotPrice * dividendDiscount * (p1 + 0.5)
                                 - strike * riskFreeDiscount * (p2 + 0.5);
                break;

            case Option.Type.Put:
                results_.value = spotPrice * dividendDiscount * (p1 - 0.5)
                                 - strike * riskFreeDiscount * (p2 - 0.5);
                break;

            default:
                Utils.QL_FAIL("unknown option type");
                break;
            }
        }
Esempio n. 23
0
        public Schedule(Date effectiveDate, Date terminationDate, Period tenor, Calendar calendar,
                        BusinessDayConvention convention, BusinessDayConvention terminationDateConvention,
                        DateGeneration.Rule rule, bool endOfMonth, Date firstDate = null, Date nextToLastDate = null)
        {
            // first save the properties
            tenor_ = tenor;
            if (calendar == null)
            {
                calendar_ = new NullCalendar();
            }
            else
            {
                calendar_ = calendar;
            }
            convention_ = convention;
            terminationDateConvention_ = terminationDateConvention;
            rule_       = rule;
            endOfMonth_ = (tenor != null && tenor < new Period(1, TimeUnit.Months)) ? false : endOfMonth;

            if (firstDate == effectiveDate)
            {
                firstDate_ = null;
            }
            else
            {
                firstDate_ = firstDate;
            }

            if (nextToLastDate == terminationDate)
            {
                nextToLastDate_ = null;
            }
            else
            {
                nextToLastDate_ = nextToLastDate;
            }

            // sanity checks
            Utils.QL_REQUIRE(terminationDate != null, () => "null termination date");

            // in many cases (e.g. non-expired bonds) the effective date is not
            // really necessary. In these cases a decent placeholder is enough
            if (effectiveDate == null && firstDate == null && rule == DateGeneration.Rule.Backward)
            {
                Date evalDate = Settings.evaluationDate();
                Utils.QL_REQUIRE(evalDate < terminationDate, () => "null effective date");
                int y;
                if (nextToLastDate != null)
                {
                    y             = (nextToLastDate - evalDate) / 366 + 1;
                    effectiveDate = nextToLastDate - new Period(y, TimeUnit.Years);
                }
                else
                {
                    y             = (terminationDate - evalDate) / 366 + 1;
                    effectiveDate = terminationDate - new Period(y, TimeUnit.Years);
                }
            }
            else
            {
                Utils.QL_REQUIRE(effectiveDate != null, () => "null effective date");
            }

            Utils.QL_REQUIRE(effectiveDate < terminationDate, () =>
                             "effective date (" + effectiveDate +
                             ") later than or equal to termination date (" +
                             terminationDate + ")"
                             );

            if (tenor_.length() == 0)
            {
                rule_ = DateGeneration.Rule.Zero;
            }
            else
            {
                Utils.QL_REQUIRE(tenor.length() > 0, () => "non positive tenor (" + tenor + ") not allowed");
            }

            if (firstDate_ != null)
            {
                switch (rule_.Value)
                {
                case DateGeneration.Rule.Backward:
                case DateGeneration.Rule.Forward:
                    Utils.QL_REQUIRE(firstDate_ > effectiveDate &&
                                     firstDate_ < terminationDate, () =>
                                     "first date (" + firstDate_ + ") out of effective-termination date range [" +
                                     effectiveDate + ", " + terminationDate + ")");
                    // we should ensure that the above condition is still verified after adjustment
                    break;

                case DateGeneration.Rule.ThirdWednesday:
                    Utils.QL_REQUIRE(IMM.isIMMdate(firstDate_, false), () => "first date (" + firstDate_ + ") is not an IMM date");
                    break;

                case DateGeneration.Rule.Zero:
                case DateGeneration.Rule.Twentieth:
                case DateGeneration.Rule.TwentiethIMM:
                case DateGeneration.Rule.OldCDS:
                case DateGeneration.Rule.CDS:
                    Utils.QL_FAIL("first date incompatible with " + rule_.Value + " date generation rule");
                    break;

                default:
                    Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                    break;
                }
            }

            if (nextToLastDate_ != null)
            {
                switch (rule_.Value)
                {
                case DateGeneration.Rule.Backward:
                case DateGeneration.Rule.Forward:
                    Utils.QL_REQUIRE(nextToLastDate_ > effectiveDate && nextToLastDate_ < terminationDate, () =>
                                     "next to last date (" + nextToLastDate_ + ") out of effective-termination date range (" +
                                     effectiveDate + ", " + terminationDate + "]");
                    // we should ensure that the above condition is still verified after adjustment
                    break;

                case DateGeneration.Rule.ThirdWednesday:
                    Utils.QL_REQUIRE(IMM.isIMMdate(nextToLastDate_, false), () => "next-to-last date (" + nextToLastDate_ +
                                     ") is not an IMM date");
                    break;

                case DateGeneration.Rule.Zero:
                case DateGeneration.Rule.Twentieth:
                case DateGeneration.Rule.TwentiethIMM:
                case DateGeneration.Rule.OldCDS:
                case DateGeneration.Rule.CDS:
                    Utils.QL_FAIL("next to last date incompatible with " + rule_.Value + " date generation rule");
                    break;

                default:
                    Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                    break;
                }
            }

            // calendar needed for endOfMonth adjustment
            Calendar nullCalendar = new NullCalendar();
            int      periods = 1;
            Date     seed = new Date(), exitDate = new Date();

            switch (rule_.Value)
            {
            case DateGeneration.Rule.Zero:
                tenor_ = new Period(0, TimeUnit.Years);
                dates_.Add(effectiveDate);
                dates_.Add(terminationDate);
                isRegular_.Add(true);
                break;

            case DateGeneration.Rule.Backward:
                dates_.Add(terminationDate);

                seed = terminationDate;
                if (nextToLastDate_ != null)
                {
                    dates_.Insert(0, nextToLastDate_);
                    Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp != nextToLastDate_)
                    {
                        isRegular_.Insert(0, false);
                    }
                    else
                    {
                        isRegular_.Insert(0, true);
                    }
                    seed = nextToLastDate_;
                }
                exitDate = effectiveDate;
                if (firstDate_ != null)
                {
                    exitDate = firstDate_;
                }

                while (true)
                {
                    Date temp = nullCalendar.advance(seed, -periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp < exitDate)
                    {
                        if (firstDate_ != null && (calendar_.adjust(dates_.First(), convention_) !=
                                                   calendar_.adjust(firstDate_, convention_)))
                        {
                            dates_.Insert(0, firstDate_);
                            isRegular_.Insert(0, false);
                        }
                        break;
                    }
                    else
                    {
                        // skip dates that would result in duplicates
                        // after adjustment
                        if (calendar_.adjust(dates_.First(), convention_) != calendar_.adjust(temp, convention_))
                        {
                            dates_.Insert(0, temp);
                            isRegular_.Insert(0, true);
                        }
                        ++periods;
                    }
                }

                if (calendar_.adjust(dates_.First(), convention) != calendar_.adjust(effectiveDate, convention))
                {
                    dates_.Insert(0, effectiveDate);
                    isRegular_.Insert(0, false);
                }

                break;

            case DateGeneration.Rule.Twentieth:
            case DateGeneration.Rule.TwentiethIMM:
            case DateGeneration.Rule.ThirdWednesday:
            case DateGeneration.Rule.OldCDS:
            case DateGeneration.Rule.CDS:
                Utils.QL_REQUIRE(!endOfMonth, () => "endOfMonth convention incompatible with " + rule_.Value + " date generation rule");
                goto case DateGeneration.Rule.Forward;                  // fall through

            case DateGeneration.Rule.Forward:
                if (rule_.Value == DateGeneration.Rule.CDS)
                {
                    dates_.Add(previousTwentieth(effectiveDate, DateGeneration.Rule.CDS));
                }
                else
                {
                    dates_.Add(effectiveDate);
                }

                seed = dates_.Last();
                if (firstDate_ != null)
                {
                    dates_.Add(firstDate_);
                    Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp != firstDate_)
                    {
                        isRegular_.Add(false);
                    }
                    else
                    {
                        isRegular_.Add(true);
                    }
                    seed = firstDate_;
                }
                else if (rule_.Value == DateGeneration.Rule.Twentieth ||
                         rule_.Value == DateGeneration.Rule.TwentiethIMM ||
                         rule_.Value == DateGeneration.Rule.OldCDS ||
                         rule_.Value == DateGeneration.Rule.CDS)
                {
                    Date next20th = nextTwentieth(effectiveDate, rule_.Value);
                    if (rule_ == DateGeneration.Rule.OldCDS)
                    {
                        // distance rule inforced in natural days
                        long stubDays = 30;
                        if (next20th - effectiveDate < stubDays)
                        {
                            // +1 will skip this one and get the next
                            next20th = nextTwentieth(next20th + 1, rule_.Value);
                        }
                    }
                    if (next20th != effectiveDate)
                    {
                        dates_.Add(next20th);
                        isRegular_.Add(false);
                        seed = next20th;
                    }
                }

                exitDate = terminationDate;
                if (nextToLastDate_ != null)
                {
                    exitDate = nextToLastDate_;
                }
                while (true)
                {
                    Date temp = nullCalendar.advance(seed, periods * tenor_, convention_, endOfMonth_.Value);
                    if (temp > exitDate)
                    {
                        if (nextToLastDate_ != null &&
                            (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(nextToLastDate_, convention_)))
                        {
                            dates_.Add(nextToLastDate_);
                            isRegular_.Add(false);
                        }
                        break;
                    }
                    else
                    {
                        // skip dates that would result in duplicates
                        // after adjustment
                        if (calendar_.adjust(dates_.Last(), convention_) != calendar_.adjust(temp, convention_))
                        {
                            dates_.Add(temp);
                            isRegular_.Add(true);
                        }
                        ++periods;
                    }
                }

                if (calendar_.adjust(dates_.Last(), terminationDateConvention_.Value) !=
                    calendar_.adjust(terminationDate, terminationDateConvention_.Value))
                {
                    if (rule_.Value == DateGeneration.Rule.Twentieth ||
                        rule_.Value == DateGeneration.Rule.TwentiethIMM ||
                        rule_.Value == DateGeneration.Rule.OldCDS ||
                        rule_.Value == DateGeneration.Rule.CDS)
                    {
                        dates_.Add(nextTwentieth(terminationDate, rule_.Value));
                        isRegular_.Add(true);
                    }
                    else
                    {
                        dates_.Add(terminationDate);
                        isRegular_.Add(false);
                    }
                }
                break;

            default:
                Utils.QL_FAIL("unknown rule (" + rule_.Value + ")");
                break;
            }

            // adjustments
            if (rule_ == DateGeneration.Rule.ThirdWednesday)
            {
                for (int i = 1; i < dates_.Count - 1; ++i)
                {
                    dates_[i] = Date.nthWeekday(3, DayOfWeek.Wednesday, dates_[i].Month, dates_[i].Year);
                }
            }

            if (endOfMonth && calendar_.isEndOfMonth(seed))
            {
                // adjust to end of month
                if (convention_ == BusinessDayConvention.Unadjusted)
                {
                    for (int i = 1; i < dates_.Count - 1; ++i)
                    {
                        dates_[i] = Date.endOfMonth(dates_[i]);
                    }
                }
                else
                {
                    for (int i = 1; i < dates_.Count - 1; ++i)
                    {
                        dates_[i] = calendar_.endOfMonth(dates_[i]);
                    }
                }
                if (terminationDateConvention_ != BusinessDayConvention.Unadjusted)
                {
                    dates_[0] = calendar_.endOfMonth(dates_.First());
                    dates_[dates_.Count - 1] = calendar_.endOfMonth(dates_.Last());
                }
                else
                {
                    // the termination date is the first if going backwards,
                    // the last otherwise.
                    if (rule_ == DateGeneration.Rule.Backward)
                    {
                        dates_[dates_.Count - 1] = Date.endOfMonth(dates_.Last());
                    }
                    else
                    {
                        dates_[0] = Date.endOfMonth(dates_.First());
                    }
                }
            }
            else
            {
                // first date not adjusted for CDS schedules
                if (rule_ != DateGeneration.Rule.OldCDS)
                {
                    dates_[0] = calendar_.adjust(dates_[0], convention_);
                }
                for (int i = 1; i < dates_.Count - 1; ++i)
                {
                    dates_[i] = calendar_.adjust(dates_[i], convention_);
                }

                // termination date is NOT adjusted as per ISDA specifications, unless otherwise specified in the
                // confirmation of the deal or unless we're creating a CDS schedule
                if (terminationDateConvention_.Value != BusinessDayConvention.Unadjusted ||
                    rule_.Value == DateGeneration.Rule.Twentieth ||
                    rule_.Value == DateGeneration.Rule.TwentiethIMM ||
                    rule_.Value == DateGeneration.Rule.OldCDS ||
                    rule_.Value == DateGeneration.Rule.CDS)
                {
                    dates_[dates_.Count - 1] = calendar_.adjust(dates_.Last(), terminationDateConvention_.Value);
                }
            }

            // final safety checks to remove duplicated last dates, if any
            // it can happen if EOM is applied to two near dates
            if (dates_.Count >= 2 && dates_[dates_.Count - 2] >= dates_.Last())
            {
                isRegular_[dates_.Count - 2] = (dates_[dates_.Count - 2] == dates_.Last());
                dates_[dates_.Count - 2]     = dates_.Last();

                dates_.RemoveAt(dates_.Count - 1);
                isRegular_.RemoveAt(isRegular_.Count - 1);
            }

            if (dates_.Count >= 2 && dates_[1] <= dates_.First())
            {
                isRegular_[1] = (dates_[1] == dates_.First());
                dates_[1]     = dates_.First();
                dates_.RemoveAt(0);
                isRegular_.RemoveAt(0);
            }

            Utils.QL_REQUIRE(dates_.Count > 1,
                             () => "degenerate single date (" + dates_[0] + ") schedule" +
                             "\n seed date: " + seed +
                             "\n exit date: " + exitDate +
                             "\n effective date: " + effectiveDate +
                             "\n first date: " + firstDate +
                             "\n next to last date: " + nextToLastDate +
                             "\n termination date: " + terminationDate +
                             "\n generation rule: " + rule_.Value +
                             "\n end of month: " + endOfMonth_.Value);
        }
Esempio n. 24
0
File: ASX.cs Progetto: igitur/qlnet
        /*! returns the ASX code for the given date
         * (e.g. M5 for June 12th, 2015).
         */
        public static String code(Date date)
        {
            Utils.QL_REQUIRE(isASXdate(date, false), () => date + " is not an ASX date");

            String ASXcode = String.Empty;
            String y       = (date.year() % 10).ToString();

            switch ((Month)date.month())
            {
            case Month.January:
                ASXcode = 'F' + y;
                break;

            case Month.February:
                ASXcode = 'G' + y;
                break;

            case Month.March:
                ASXcode = 'H' + y;
                break;

            case Month.April:
                ASXcode = 'J' + y;
                break;

            case Month.May:
                ASXcode = 'K' + y;
                break;

            case Month.June:
                ASXcode = 'M' + y;
                break;

            case Month.July:
                ASXcode = 'N' + y;
                break;

            case Month.August:
                ASXcode = 'Q' + y;
                break;

            case Month.September:
                ASXcode = 'U' + y;
                break;

            case Month.October:
                ASXcode = 'V' + y;
                break;

            case Month.November:
                ASXcode = 'X' + y;
                break;

            case Month.December:
                ASXcode = 'Z' + y;
                break;

            default:
                Utils.QL_FAIL("not an ASX month (and it should have been)");
                break;
            }

            return(ASXcode);
        }
Esempio n. 25
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);
        }
Esempio n. 26
0
File: ASX.cs Progetto: igitur/qlnet
        /*! returns the ASX date for the given ASX code
         * (e.g. June 12th, 2015 for M5).
         *
         * \warning It raises an exception if the input
         *          string is not an ASX code
         */
        public static Date date(String asxCode, Date refDate = null)
        {
            Utils.QL_REQUIRE(isASXcode(asxCode, false), () =>
                             asxCode + " is not a valid ASX code");

            Date referenceDate = refDate ?? Settings.evaluationDate();

            String code = asxCode.ToUpper();
            String ms   = code.Substring(0, 1);
            Month  m    = 0;

            if (ms == "F")
            {
                m = Month.January;
            }
            else if (ms == "G")
            {
                m = Month.February;
            }
            else if (ms == "H")
            {
                m = Month.March;
            }
            else if (ms == "J")
            {
                m = Month.April;
            }
            else if (ms == "K")
            {
                m = Month.May;
            }
            else if (ms == "M")
            {
                m = Month.June;
            }
            else if (ms == "N")
            {
                m = Month.July;
            }
            else if (ms == "Q")
            {
                m = Month.August;
            }
            else if (ms == "U")
            {
                m = Month.September;
            }
            else if (ms == "V")
            {
                m = Month.October;
            }
            else if (ms == "X")
            {
                m = Month.November;
            }
            else if (ms == "Z")
            {
                m = Month.December;
            }
            else
            {
                Utils.QL_FAIL("invalid ASX month letter");
            }

//       Year y = boost::lexical_cast<Year>(); // lexical_cast causes compilation errors with x64

            int y = int.Parse(code.Substring(1, 1));

            /* year<1900 are not valid QuantLib years: to avoid a run-time
             * exception few lines below we need to add 10 years right away */
            if (y == 0 && referenceDate.year() <= 1909)
            {
                y += 10;
            }
            int referenceYear = (referenceDate.year() % 10);

            y += referenceDate.year() - referenceYear;
            Date result = ASX.nextDate(new Date(1, m, y), false);

            if (result < referenceDate)
            {
                return(ASX.nextDate(new Date(1, m, y + 10), false));
            }

            return(result);
        }
Esempio n. 27
0
        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);
        }
Esempio n. 28
0
 public override double primitive(double d)
 {
     Utils.QL_FAIL("Primitive calculation not implemented for kernel interpolation");
     return(0);
 }
Esempio n. 29
0
 public void visit(Payoff p)
 {
     Utils.QL_FAIL("unsupported payoff type: " + p.name());
 }
Esempio n. 30
0
        public void calculate()
        {
            // we might have to call initialize even if the curve is initialized
            // and not moving, just because helpers might be date relative and change
            // with evaluation date change.
            // anyway it makes little sense to use date relative helpers with a
            // non-moving curve if the evaluation date changes
            if (!initialized_ || ts_.moving_)
            {
                initialize();
            }

            // setup helpers
            for (int j = firstAliveHelper_; j < n_; ++j)
            {
                BootstrapHelper <U> helper = ts_.instruments_[j];
                // check for valid quote
                Utils.QL_REQUIRE(helper.quote().link.isValid(), () =>
                                 (j + 1) + " instrument (maturity: " +
                                 helper.pillarDate() + ") has an invalid quote");
                // don't try this at home!
                // This call creates helpers, and removes "const".
                // There is a significant interaction with observability.
                ts_.setTermStructure(ts_.instruments_[j]);
            }

            List <double> times         = ts_.times_;
            List <double> data          = ts_.data_;
            double        accuracy      = ts_.accuracy_;
            int           maxIterations = ts_.maxIterations() - 1;

            // there might be a valid curve state to use as guess
            bool validData = validCurve_;

            for (int iteration = 0; ; ++iteration)
            {
                previousData_ = ts_.data_;

                for (int i = 1; i <= alive_; ++i)
                {
                    // pillar loop

                    // bracket root and calculate guess
                    double min   = ts_.minValueAfter(i, ts_, validData, firstAliveHelper_);
                    double max   = ts_.maxValueAfter(i, ts_, validData, firstAliveHelper_);
                    double guess = ts_.guess(i, ts_, validData, firstAliveHelper_);
                    // adjust guess if needed
                    if (guess >= max)
                    {
                        guess = max - (max - min) / 5.0;
                    }
                    else if (guess <= min)
                    {
                        guess = min + (max - min) / 5.0;
                    }

                    // extend interpolation if needed
                    if (!validData)
                    {
                        try
                        {
                            // extend interpolation a point at a time
                            // including the pillar to be boostrapped
                            ts_.interpolation_ = ts_.interpolator_.interpolate(ts_.times_, i + 1, ts_.data_);
                            //ts_.interpolation_ = ts_.interpolator_.interpolate(times, times.Count, data);
                        }
                        catch (Exception)
                        {
                            if (!ts_.interpolator_.global)
                            {
                                throw;                                 // no chance to fix it in a later iteration
                            }
                            // otherwise use Linear while the target
                            // interpolation is not usable yet
                            ts_.interpolation_ = new Linear().interpolate(ts_.times_, i + 1, ts_.data_);
                            //ts_.interpolation_ = new Linear().interpolate(times, times.Count, data);
                        }
                        ts_.interpolation_.update();
                    }

                    try
                    {
                        var error = new BootstrapError <T, U>(ts_, ts_.instruments_[i - 1], i);
                        if (validData)
                        {
                            ts_.data_[i] = solver_.solve(error, accuracy, guess, min, max);
                        }
                        else
                        {
                            ts_.data_[i] = firstSolver_.solve(error, accuracy, guess, min, max);
                        }
                    }
                    catch (Exception e)
                    {
                        // the previous curve state could have been a bad guess
                        // let's restart without using it
                        if (validCurve_)
                        {
                            validCurve_ = validData = false;
                            continue;
                        }
                        Utils.QL_FAIL((iteration + 1) + " iteration: failed " +
                                      "at " + (i) + " alive instrument, " +
                                      "maturity " + ts_.instruments_[i - 1].pillarDate() +
                                      ", reference date " + ts_.dates_[0] +
                                      ": " + e.Message);
                    }
                }

                if (!loopRequired_)
                {
                    break;                         // no need for convergence loop
                }
                // exit condition
                double change = Math.Abs(data[1] - previousData_[1]);
                for (int i = 2; i <= alive_; ++i)
                {
                    change = Math.Max(change, Math.Abs(data[i] - previousData_[i]));
                }
                if (change <= accuracy)                    // convergence reached
                {
                    break;
                }

                Utils.QL_REQUIRE(iteration < maxIterations, () =>
                                 "convergence not reached after " + iteration +
                                 " iterations; last improvement " + change +
                                 ", required accuracy " + accuracy);
                validData = true;
            }
            validCurve_ = true;
        }