예제 #1
0
 Real BlackIborCouponPricer::optionletPrice(Option::Type optionType,
                                            Real effStrike) const {
     Date fixingDate = coupon_->fixingDate();
     if (fixingDate <= Settings::instance().evaluationDate()) {
         // the amount is determined
         Real a, b;
         if (optionType==Option::Call) {
             a = coupon_->indexFixing();
             b = effStrike;
         } else {
             a = effStrike;
             b = coupon_->indexFixing();
         }
         return std::max(a - b, 0.0)* accrualPeriod_*discount_;
     } else {
         // not yet determined, use Black model
         QL_REQUIRE(!capletVolatility().empty(),
                    "missing optionlet volatility");
         Real stdDev =
             std::sqrt(capletVolatility()->blackVariance(fixingDate,
                                                         effStrike));
         Real shift = capletVolatility()->displacement();
         bool shiftedLn =
             capletVolatility()->volatilityType() == ShiftedLognormal;
         Rate fixing =
             shiftedLn
                 ? blackFormula(optionType, effStrike, adjustedFixing(),
                                stdDev, 1.0, shift)
                 : bachelierBlackFormula(optionType, effStrike,
                                         adjustedFixing(), stdDev, 1.0);
         return fixing * accrualPeriod_ * discount_;
     }
 }
     Real HullWhiteSmileSection::volatilityImpl(Rate strike) const {
		
		boost::shared_ptr<VanillaSwap> underlying = MakeVanillaSwap(swapLength_,swapIndex_->iborIndex(),strike)
																	.withEffectiveDate(swapIndex_->valueDate(optionDate_))  
																	.withFixedLegCalendar(swapIndex_->fixingCalendar())
													                .withFixedLegDayCount(swapIndex_->dayCounter())
													                .withFixedLegTenor(swapIndex_->fixedLegTenor())
													                .withFixedLegConvention(swapIndex_->fixedLegConvention())
													                .withFixedLegTerminationDateConvention(swapIndex_->fixedLegConvention())
																	.withType(VanillaSwap::Payer);
		underlying->setPricingEngine(discountingEngine_);
		boost::shared_ptr<Exercise> exercise(new EuropeanExercise(optionDate_));
		Swaption swaption(underlying,exercise);
		swaption.setPricingEngine(jamshidianEngine_);
		Real npv = swaption.NPV();
		Real annuity = fabs(underlying->fixedLegBPS() * 10000.0);

		//Real blackNpv=blackFormula(Option::Call,strike,atm,0.30*sqrt(exerciseTime()));
		//std::cout << std::setprecision(8) << "SECTION atm=" << atm_ << " annuity=" << annuity << " npv=" << npv << " strike= " << strike << std::endl;

		Real vol=0.0;
		try {
			vol=blackFormulaImpliedStdDev(Option::Call,strike,atm_,npv,annuity) / sqrt(exerciseTime());
		} catch(QuantLib::Error) {
			//std::cout << " can not imply vol for npv " << npv << " black(300%)=" << blackFormula(Option::Call,strike,atm_,sqrt(exerciseTime())*3.00,annuity) << 
			//	" black(1%)=" << blackFormula(Option::Call,strike,atm_,sqrt(exerciseTime())*0.01,annuity) << std::endl;
			if(npv>blackFormula(Option::Call,strike,atm_,3.00,annuity)) vol =3.0/sqrt(exerciseTime());
			else vol=0.0;
		}
		return vol;

     }
 Real YoYInflationBlackCapFloorEngine::optionletImpl(Option::Type type, Rate strike,
                                                     Rate forward, Real stdDev,
                                                     Real d) const
 {
     return blackFormula(type, strike,
                         forward, stdDev, d);
 }
예제 #4
0
 Real HestonModelHelper::blackPrice(Real volatility) const {
     calculate();
     const Real stdDev = volatility * std::sqrt(maturity());
     return blackFormula(
         type_, strikePrice_ * termStructure_->discount(tau_),
         s0_->value() * dividendYield_->discount(tau_), stdDev);
 }
예제 #5
0
 Real blackFormula(const boost::shared_ptr<PlainVanillaPayoff>& payoff,
                   Real forward,
                   Real stdDev,
                   Real discount,
                   Real displacement) {
     return blackFormula(payoff->optionType(),
         payoff->strike(), forward, stdDev, discount, displacement);
 }
 Real YoYInflationUnitDisplacedBlackCapFloorEngine::optionletImpl(
                                                     Option::Type type, Rate strike,
                                                     Rate forward, Real stdDev,
                                                     Real d) const
 {
     // could use displacement parameter in blackFormula but this is clearer
     return blackFormula(type, strike+1.0,
                         forward+1.0, stdDev, d);
 }
예제 #7
0
	Real Generalized_HullWhite::discountBondOption(Option::Type type, Real strike,
		Time maturity,
		Time bondMaturity) const {

			Real _a = a();
			Real v;
			v = std::sqrt(Vp(0, maturity, bondMaturity));
			Real f = termStructure()->discount(bondMaturity);
			Real k = termStructure()->discount(maturity)*strike;

			return blackFormula(type, k, f, v);
	}
예제 #8
0
 Real SmileSection::optionPrice(Rate strike,
                                Option::Type type,
                                Real discount) const {
     Real atm = atmLevel();
     QL_REQUIRE(atm != Null<Real>(),
                "smile section must provide atm level to compute option price");
     // if lognormal or shifted lognormal,
     // for strike at -shift, return option price even if outside
     // minstrike, maxstrike interval
     if (volatilityType() == ShiftedLognormal)
         return blackFormula(type,strike,atm, fabs(strike+shift()) < QL_EPSILON ?
                         0.2 : sqrt(variance(strike)),discount,shift());
     else
         return bachelierBlackFormula(type,strike,atm,sqrt(variance(strike)),discount);
 }
예제 #9
0
    Real HullWhite::discountBondOption(Option::Type type, Real strike,
                                       Time maturity,
                                       Time bondMaturity) const {

        Real _a = a();
        Real v;
        if (_a < std::sqrt(QL_EPSILON)) {
            v = sigma()*B(maturity, bondMaturity)* std::sqrt(maturity);
        } else {
            v = sigma()*B(maturity, bondMaturity)*
                std::sqrt(0.5*(1.0 - std::exp(-2.0*_a*maturity))/_a);
        }
        Real f = termStructure()->discount(bondMaturity);
        Real k = termStructure()->discount(maturity)*strike;

        return blackFormula(type, k, f, v);
    }
예제 #10
0
    Real HullWhite::discountBondOption(Option::Type type, Real strike,
                                       Time maturity, Time bondStart,
                                       Time bondMaturity) const {

        Real _a = a();
        Real v;
        if (_a < std::sqrt(QL_EPSILON)) {
            v = sigma()*B(bondStart, bondMaturity)* std::sqrt(maturity);
        } else {
            v = sigma()/(_a*sqrt(2.0*_a)) * sqrt ( 
                   exp(-2.0*_a*(bondStart-maturity))-exp(-2.0*_a*bondStart)
                   -2.0*(exp(-_a*(bondStart+bondMaturity-2.0*maturity))-exp(-_a*(bondStart+bondMaturity)))
                   +exp(-2.0*_a*(bondMaturity-maturity))-exp(-2.0*_a*bondMaturity));
        }
        Real f = termStructure()->discount(bondMaturity);
        Real k = termStructure()->discount(bondStart)*strike;

        return blackFormula(type, k, f, v);
    }
예제 #11
0
    Real LiborForwardModel::discountBondOption(Option::Type type,
                                               Real strike, Time maturity,
                                               Time bondMaturity) const {

        const std::vector<Time> & accrualStartTimes
            = process_->accrualStartTimes();
        const std::vector<Time> & accrualEndTimes
            = process_->accrualEndTimes();

        QL_REQUIRE(   accrualStartTimes.front()<= maturity
                   && accrualStartTimes.back() >= maturity,
                   "capet maturity does not fit to the process");

        const Size i = std::lower_bound(accrualStartTimes.begin(),
                                        accrualStartTimes.end(),
                                        maturity) - accrualStartTimes.begin();

        QL_REQUIRE(   i<process_->size()
                   && std::fabs(maturity - accrualStartTimes[i])
                        < 100*std::numeric_limits<Real>::epsilon()
                   && std::fabs(bondMaturity - accrualEndTimes[i])
                        < 100*std::numeric_limits<Real>::epsilon(),
                   "irregular fixings are not (yet) supported");

        const Real tenor     = accrualEndTimes[i] - accrualStartTimes[i];
        const Real forward   = process_->initialValues()[i];
        const Real capRate   = (1.0/strike - 1.0)/tenor;
        const Volatility var = covarProxy_
            ->integratedCovariance(i, i, process_->fixingTimes()[i]);
        const DiscountFactor dis =
            process_->index()->forwardingTermStructure()->discount(bondMaturity);

        const Real black = blackFormula(
            (type==Option::Put ? Option::Call : Option::Put),
            capRate, forward, std::sqrt(var));

        const Real npv = dis * tenor * black;

        return npv / (1.0 + capRate*tenor);
    }
예제 #12
0
    CapPseudoDerivative::CapPseudoDerivative(boost::shared_ptr<MarketModel> inputModel,
        Real strike,
        Size startIndex,
        Size endIndex, Real firstDF) : firstDF_(firstDF)
    {
        QL_REQUIRE(startIndex < endIndex, "for a cap pseudo derivative the start of the cap must be before the end");
        QL_REQUIRE( endIndex <= inputModel->numberOfRates(), "for a cap pseudo derivative the end of the cap must before the end of the rates");

        Size numberCaplets = endIndex-startIndex;
        Size numberRates = inputModel->numberOfRates();
        Size factors = inputModel->numberOfFactors();
        LMMCurveState curve(inputModel->evolution().rateTimes());
        curve.setOnForwardRates(inputModel->initialRates());

        const Matrix& totalCovariance(inputModel->totalCovariance(inputModel->numberOfSteps()-1));

        std::vector<Real> displacedImpliedVols(numberCaplets);
        std::vector<Real> annuities(numberCaplets);
        std::vector<Real> initialRates(numberCaplets);
        std::vector<Real> expiries(numberCaplets);

        Real capPrice =0.0;

        Real guess=0.0;
        Real minVol = 1e10;
        Real maxVol =0.0;

        for (Size j = startIndex; j < endIndex; ++j)
        {
            Size capletIndex = j - startIndex;
            Time resetTime = inputModel->evolution().rateTimes()[j];
            expiries[capletIndex] =  resetTime;

            Real sd = std::sqrt(totalCovariance[j][j]);
            displacedImpliedVols[capletIndex] = std::sqrt(totalCovariance[j][j]/resetTime);

            Real forward = inputModel->initialRates()[j];
            initialRates[capletIndex] = forward;

            Real annuity = curve.discountRatio(j+1,0)* inputModel->evolution().rateTaus()[j]*firstDF_;
            annuities[capletIndex] = annuity;

            Real displacement =  inputModel->displacements()[j];

            guess+=  displacedImpliedVols[capletIndex]*(forward+displacement)/forward;
            minVol =std::min(minVol, displacedImpliedVols[capletIndex]);
            maxVol =std::max(maxVol, displacedImpliedVols[capletIndex]*(forward+displacement)/forward);


            Real capletPrice = blackFormula(Option::Call,
                strike,
                forward,
                sd,
                annuity,
                displacement
                );

            capPrice += capletPrice;

        }

        guess/=numberCaplets;


        for (Size step =0; step < inputModel->evolution().numberOfSteps(); ++step)
        {
            Matrix thisDerivative(numberRates,factors,0.0);

            for (Size rate =std::max(inputModel->evolution().firstAliveRate()[step],startIndex); 
                rate < endIndex; ++rate)
            {
                for (Size f=0; f < factors; ++f)
                {
                    Real expiry = inputModel->evolution().rateTimes()[rate];
                    Real volDerivative = inputModel->pseudoRoot(step)[rate][f]
                    /(displacedImpliedVols[rate-startIndex]*expiry);
                    Real capletVega = blackFormulaVolDerivative(strike,inputModel->initialRates()[rate],
                        displacedImpliedVols[rate-startIndex]*std::sqrt(expiry),
                        expiry,
                        annuities[rate-startIndex],
                        inputModel->displacements()[rate]);

                    // note that the cap derivative  is equal to one of the caplet ones so we lose a loop
                    thisDerivative[rate][f] = volDerivative*capletVega;
                }
            }

            priceDerivatives_.push_back(thisDerivative);

        }

        QuickCap capPricer(strike, annuities, initialRates, expiries,capPrice);

        Size maxEvaluations = 1000;
        Real accuracy = 1E-6;

        Brent solver;
        solver.setMaxEvaluations(maxEvaluations);
        impliedVolatility_ =   solver.solve(capPricer,accuracy,guess,minVol*0.99,maxVol*1.01);
      
            

        vega_ = capPricer.vega(impliedVolatility_);



        for (Size step =0; step < inputModel->evolution().numberOfSteps(); ++step)
        {
            Matrix thisDerivative(numberRates,factors,0.0);

            for (Size rate =std::max(inputModel->evolution().firstAliveRate()[step],startIndex); 
                rate < endIndex; ++rate)
            {
                for (Size f=0; f < factors; ++f)
                {
                   
                    thisDerivative[rate][f] = priceDerivatives_[step][rate][f]/vega_;
                }
            }

            volatilityDerivatives_.push_back(thisDerivative);

        }



    }
예제 #13
0
 Real value(const Option::Type type, const Real strike,
            const Real atmForward, const Real stdDev, const Real annuity,
            const Real displacement) {
     return blackFormula(type, strike, atmForward, stdDev, annuity,
                         displacement);
 }
예제 #14
0
    void JuQuadraticApproximationEngine::calculate() const {

        QL_REQUIRE(arguments_.exercise->type() == Exercise::American,
                   "not an American Option");

        boost::shared_ptr<AmericanExercise> ex =
            boost::dynamic_pointer_cast<AmericanExercise>(arguments_.exercise);
        QL_REQUIRE(ex, "non-American exercise given");
        QL_REQUIRE(!ex->payoffAtExpiry(),
                   "payoff at expiry not handled");

        boost::shared_ptr<StrikedTypePayoff> payoff =
            boost::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
        QL_REQUIRE(payoff, "non-striked payoff given");

        Real variance = process_->blackVolatility()->blackVariance(
            ex->lastDate(), payoff->strike());
        DiscountFactor dividendDiscount = process_->dividendYield()->discount(
            ex->lastDate());
        DiscountFactor riskFreeDiscount = process_->riskFreeRate()->discount(
            ex->lastDate());
        Real spot = process_->stateVariable()->value();
        QL_REQUIRE(spot > 0.0, "negative or null underlying given");
        Real forwardPrice = spot * dividendDiscount / riskFreeDiscount;
        BlackCalculator black(payoff, forwardPrice,
                              std::sqrt(variance), riskFreeDiscount);

        if (dividendDiscount>=1.0 && payoff->optionType()==Option::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()->dayCounter();
            DayCounter divdc = process_->dividendYield()->dayCounter();
            DayCounter voldc = process_->blackVolatility()->dayCounter();
            Time t =
                rfdc.yearFraction(process_->riskFreeRate()->referenceDate(),
                                  arguments_.exercise->lastDate());
            results_.rho = black.rho(t);

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

            t = voldc.yearFraction(process_->blackVolatility()->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;
            NormalDistribution normalDist;

            Real tolerance = 1e-6;
            Real Sk = BaroneAdesiWhaleyApproximationEngine::criticalPrice(
                payoff, riskFreeDiscount, dividendDiscount, variance,
                tolerance);

            Real forwardSk = Sk * dividendDiscount / riskFreeDiscount;

            Real alpha = -2.0*std::log(riskFreeDiscount)/(variance);
            Real beta = 2.0*std::log(dividendDiscount/riskFreeDiscount)/
                                                (variance);
            Real h = 1 - riskFreeDiscount;
            Real phi;
            switch (payoff->optionType()) {
                case Option::Call:
                    phi = 1;
                    break;
                case Option::Put:
                    phi = -1;
                    break;
                default:
                  QL_FAIL("unknown option type");
            }
            //it can throw: to be fixed
            // FLOATING_POINT_EXCEPTION
            Real temp_root = std::sqrt ((beta-1)*(beta-1) + (4*alpha)/h);
            Real lambda = (-(beta-1) + phi * temp_root) / 2;
            Real lambda_prime = - phi * alpha / (h*h * temp_root);

            Real black_Sk = blackFormula(payoff->optionType(), payoff->strike(),
                                         forwardSk, std::sqrt(variance)) * riskFreeDiscount;
            Real hA = phi * (Sk - payoff->strike()) - black_Sk;

            Real d1_Sk = (std::log(forwardSk/payoff->strike()) + 0.5*variance)
                /std::sqrt(variance);
            Real d2_Sk = d1_Sk - std::sqrt(variance);
            Real part1 = forwardSk * normalDist(d1_Sk) /
                                        (alpha * std::sqrt(variance));
            Real part2 = - phi * forwardSk * cumNormalDist(phi * d1_Sk) *
                      std::log(dividendDiscount) / std::log(riskFreeDiscount);
            Real part3 = + phi * payoff->strike() * cumNormalDist(phi * d2_Sk);
            Real V_E_h = part1 + part2 + part3;

            Real b = (1-h) * alpha * lambda_prime / (2*(2*lambda + beta - 1));
            Real c = - ((1 - h) * alpha / (2 * lambda + beta - 1)) *
                (V_E_h / (hA) + 1 / h + lambda_prime / (2*lambda + beta - 1));
            Real temp_spot_ratio = std::log(spot / Sk);
            Real chi = temp_spot_ratio * (b * temp_spot_ratio + c);

            if (phi*(Sk-spot) > 0) {
                results_.value = black.value() +
                    hA * std::pow((spot/Sk), lambda) / (1 - chi);
            } else {
                results_.value = phi * (spot - payoff->strike());
            }

            Real temp_chi_prime = (2 * b / spot) * std::log(spot/Sk);
            Real chi_prime = temp_chi_prime + c / spot;
            Real chi_double_prime = 2*b/(spot*spot)
                                    - temp_chi_prime / spot
                                    - c / (spot*spot);
            results_.delta = phi * dividendDiscount * cumNormalDist (phi * d1_Sk)
                + (lambda / (spot * (1 - chi)) + chi_prime / ((1 - chi)*(1 - chi))) *
                (phi * (Sk - payoff->strike()) - black_Sk) * std::pow((spot/Sk), lambda);

            results_.gamma = phi * dividendDiscount * normalDist (phi*d1_Sk) /
                                        (spot * std::sqrt(variance))
                           + (2 * lambda * chi_prime / (spot * (1 - chi) * (1 - chi))
                              + 2 * chi_prime * chi_prime / ((1 - chi) * (1 - chi) * (1 - chi))
                              + chi_double_prime / ((1 - chi) * (1 - chi))
                              + lambda * (1 - lambda) / (spot * spot * (1 - chi)))
                            * (phi * (Sk - payoff->strike()) - black_Sk)
                                 * std::pow((spot/Sk), lambda);

        } // end of "early exercise can be optimal"
    }
예제 #15
0
    void BlackCapFloorEngine::calculate() const {
        Real value = 0.0;
        Real vega = 0.0;
        Size optionlets = arguments_.startDates.size();
        std::vector<Real> values(optionlets, 0.0);
        std::vector<Real> vegas(optionlets, 0.0);
        std::vector<Real> stdDevs(optionlets, 0.0);
        CapFloor::Type type = arguments_.type;
        Date today = vol_->referenceDate();
        Date settlement = discountCurve_->referenceDate();

        for (Size i=0; i<optionlets; ++i) {
            Date paymentDate = arguments_.endDates[i];
            // handling of settlementDate, npvDate and includeSettlementFlows
            // should be implemented.
            // For the time being just discard expired caplets
            if (paymentDate > settlement) {
                DiscountFactor d = arguments_.nominals[i] *
                                   arguments_.gearings[i] *
                                   discountCurve_->discount(paymentDate) *
                                   arguments_.accrualTimes[i];

                Rate forward = arguments_.forwards[i];

                Date fixingDate = arguments_.fixingDates[i];
                Time sqrtTime = 0.0;
                if (fixingDate > today)
                    sqrtTime = std::sqrt(vol_->timeFromReference(fixingDate));

                if (type == CapFloor::Cap || type == CapFloor::Collar) {
                    Rate strike = arguments_.capRates[i];
                    if (sqrtTime>0.0) {
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
                                                                   strike));
                        vegas[i] = blackFormulaStdDevDerivative(strike,
                            forward, stdDevs[i], d, displacement_) * sqrtTime;
                    }
                    // include caplets with past fixing date
                    values[i] = blackFormula(Option::Call,
                        strike, forward, stdDevs[i], d, displacement_);
                }
                if (type == CapFloor::Floor || type == CapFloor::Collar) {
                    Rate strike = arguments_.floorRates[i];
                    Real floorletVega = 0.0;
                    if (sqrtTime>0.0) {
                        stdDevs[i] = std::sqrt(vol_->blackVariance(fixingDate,
                                                                   strike));
                        floorletVega = blackFormulaStdDevDerivative(strike,
                            forward, stdDevs[i], d, displacement_) * sqrtTime;
                    }
                    Real floorlet = blackFormula(Option::Put,
                        strike, forward, stdDevs[i], d, displacement_);
                    if (type == CapFloor::Floor) {
                        values[i] = floorlet;
                        vegas[i] = floorletVega;
                    } else {
                        // a collar is long a cap and short a floor
                        values[i] -= floorlet;
                        vegas[i] -= floorletVega;
                    }
                }
                value += values[i];
                vega += vegas[i];
            }
        }
        results_.value = value;
        results_.additionalResults["vega"] = vega;

        results_.additionalResults["optionletsPrice"] = values;
        results_.additionalResults["optionletsVega"] = vegas;
        results_.additionalResults["optionletsAtmForward"] = arguments_.forwards;
        if (type != CapFloor::Collar)
            results_.additionalResults["optionletsStdDev"] = stdDevs;
    }
예제 #16
0
    void KahaleSmileSection::compute() {

        std::pair<Size,Size> afIdx = ssutils_->arbitragefreeIndices();
        leftIndex_ = afIdx.first;
        rightIndex_ = afIdx.second;

        if(deleteArbitragePoints_) {
            while(leftIndex_>1 || rightIndex_<k_.size()-1) {
                
                ssutils_ = boost::shared_ptr<SmileSectionUtils>(new SmileSectionUtils(*source_,moneynessGrid_,f_));
                std::pair<Size,Size> afIdx = ssutils_->arbitragefreeIndices();

                leftIndex_ = afIdx.first;
                rightIndex_ = afIdx.second;

                QL_REQUIRE(rightIndex_>leftIndex_,
                           "arbitrage free region must at least contain two points (only index is " << leftIndex_ << ")");
            
                if(leftIndex_>1) {
                    moneynessGrid_.erase(moneynessGrid_.begin()+leftIndex_-1);
                    k_.erase(k_.begin()+leftIndex_-1);
                    c_.erase(c_.begin()+leftIndex_-1);
                    leftIndex_--;
                    rightIndex_--; 
                }

                if(rightIndex_<k_.size()-1) {
                    moneynessGrid_.erase(moneynessGrid_.begin()+rightIndex_+1);
                    k_.erase(k_.begin()+rightIndex_+1);
                    c_.erase(c_.begin()+rightIndex_+1);
                    rightIndex_--;
                }
            }
        }

        cFunctions_ = std::vector<boost::shared_ptr<cFunction> >(rightIndex_-leftIndex_+2);

        // extrapolation in the leftmost interval

        Brent brent;
        bool success;
        Real secl = 0.0;

        do {
            success=true;
            try {
                Real k1 = k_[leftIndex_];
                Real c1 = c_[leftIndex_];
                Real c0 = c_[0];
                secl = (c_[leftIndex_]-c_[0]) / (k_[leftIndex_]-k_[0]);
                Real sec = (c_[leftIndex_+1]-c_[leftIndex_]) / (k_[leftIndex_+1]-k_[leftIndex_]);
                Real c1p;
                if(interpolate_) c1p=(secl+sec)/2;
                else {
                    c1p=(blackFormula(Option::Call, k1+gap_, f_, sqrt(source_->variance(k1+gap_)))-
                         blackFormula(Option::Call, k1, f_, sqrt(source_->variance(k1))))/gap_;
                    QL_REQUIRE(secl < c1p && c1p <= 0.0,"dummy"); 
                    // can not extrapolate so throw exception which is caught below
                }
                sHelper1 sh1(k1,c0,c1,c1p);
                Real s = brent.solve(sh1,QL_KAHALE_ACC,0.20,0.00,QL_KAHALE_SMAX); // numerical parameters hardcoded here
                sh1(s);
                boost::shared_ptr<cFunction> cFct1(new cFunction(sh1.f_,s,0.0,sh1.b_));
                cFunctions_[0]=cFct1;
            } catch(...) {
                leftIndex_++;
                success=false;
            }
        } while(!success && leftIndex_ < rightIndex_);

        QL_REQUIRE(leftIndex_ < rightIndex_, "can not extrapolate to left, right index of af region reached (" 
                   << rightIndex_ << ")");

        // interpolation

        Real cp0 = 0.0, cp1 = 0.0;

        if(interpolate_) {

            for(Size i = leftIndex_; i<rightIndex_; i++) {
                Real k0 = k_[i];
                Real k1 = k_[i+1];
                Real c0 = c_[i];
                Real c1 = c_[i+1];
                Real sec = (c_[i+1]-c_[i]) / (k_[i+1]-k_[i]);
                if(i==leftIndex_) cp0 = leftIndex_ > 0 ? (secl + sec) / 2.0 : sec;
                Real secr;
                if(i==rightIndex_-1) secr=0.0;
                else secr = (c_[i+2]-c_[i+1]) / (k_[i+2]-k_[i+1]);
                cp1 = (sec+secr) / 2.0;
                aHelper ah(k0,k1,c0,c1,cp0,cp1);
                Real a;
                try {
                    a = brent.solve(ah,QL_KAHALE_ACC,0.5*(cp1+(1.0+cp0)),cp1+QL_KAHALE_EPS,1.0+cp0-QL_KAHALE_EPS);
                    // numerical parameters hardcoded here
                } catch(...) {
                    // from theory there must exist a zero. if the solver does not find it, it most
                    // probably lies close one of the interval bounds. Just choose the better bound
                    // and relax the accuracy. This does not matter in practice usually.
                    Real la = std::fabs(ah(cp1+QL_KAHALE_EPS));
                    Real ra = std::fabs(ah(1.0+cp0-QL_KAHALE_EPS));
                    if( la < QL_KAHALE_ACC_RELAX || ra < QL_KAHALE_ACC_RELAX) { // tolerance hardcoded here
                        a = la < ra ? cp1+QL_KAHALE_EPS : 1.0+cp0-QL_KAHALE_EPS;
                    }
                    else
                        QL_FAIL("can not interpolate at index " << i);

                }
                ah(a);
                boost::shared_ptr<cFunction> cFct(new cFunction(ah.f_,ah.s_,a,ah.b_));
                cFunctions_[leftIndex_ > 0 ? i-leftIndex_+1 : 0]=cFct;
                cp0=cp1;
            }

        }

        // extrapolation of right wing

        do {
            success=true;
            try {
                Real k0 = k_[rightIndex_];
                Real c0 = c_[rightIndex_];
                Real cp0;
                if(interpolate_) cp0 = 0.5*(c_[rightIndex_]-c_[rightIndex_-1])/(k_[rightIndex_]-k_[rightIndex_-1]);
                else {
                    cp0=(blackFormula(Option::Call, k0, f_, sqrt(source_->variance(k0)))-
                         blackFormula(Option::Call, k0-gap_, f_, sqrt(source_->variance(k0-gap_))))/gap_;

                }
                boost::shared_ptr<cFunction> cFct;
                if(exponentialExtrapolation_) {
                    cFct = boost::shared_ptr<cFunction>(new cFunction(-cp0/c0 ,std::log(c0)-cp0/c0*k0));
                }
                else {
                    sHelper sh(k0,c0,cp0);
                    Real s;
                    s = brent.solve(sh,QL_KAHALE_ACC,0.20,0.0,QL_KAHALE_SMAX);  // numerical parameters hardcoded here
                    sh(s);
                    cFct = boost::shared_ptr<cFunction>(new cFunction(sh.f_,s,0.0,0.0));
                }
                cFunctions_[rightIndex_-leftIndex_+1]=cFct;
            } catch (...) {
                rightIndex_--;
                success=false;
            }
        } while(!success && rightIndex_ > leftIndex_);

        QL_REQUIRE(leftIndex_ < rightIndex_, "can not extrapolate to right, left index of af region reached (" << 
                   leftIndex_ << ")");


    }
예제 #17
0
    void BlackSwaptionEngine::calculate() const {
        static const Spread 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;

        Rate strike = swap.fixedRate();

        // using the discounting curve
        // swap.iborIndex() might be using a different forwarding curve
        swap.setPricingEngine(boost::shared_ptr<PricingEngine>(new
            DiscountingSwapEngine(discountCurve_, false)));
        Rate 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()!=0.0) {
            Spread correction = swap.spread() *
                std::fabs(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(boost::shared_ptr<PricingEngine>(
                           new DiscountingSwapEngine(discountCurve_, false)));
        Real annuity;
        switch(arguments_.settlementType) {
          case Settlement::Physical: {
              annuity = std::fabs(swap.fixedLegBPS())/basisPoint;
              break;
          }
          case Settlement::Cash: {
              const Leg& fixedLeg = swap.fixedLeg();
              boost::shared_ptr<FixedRateCoupon> firstCoupon =
                  boost::dynamic_pointer_cast<FixedRateCoupon>(fixedLeg[0]);
              DayCounter dayCount = firstCoupon->dayCounter();
              Real fixedLegCashBPS =
                  CashFlows::bps(fixedLeg,
                                 InterestRate(atmForward, dayCount, Compounded, Annual),
                                 false, discountCurve_->referenceDate()) ;
              annuity = std::fabs(fixedLegCashBPS/basisPoint);
              break;
          }
          default:
            QL_FAIL("unknown settlement type");
        }
        results_.additionalResults["annuity"] = annuity;

        // the swap length calculation might be improved using the value date
        // of the exercise date
        Time swapLength =  vol_->swapLength(exerciseDate,
                                                   arguments_.floatingPayDates.back());
        results_.additionalResults["swapLength"] = swapLength;

        Real variance = vol_->blackVariance(exerciseDate,
                                                   swapLength,
                                                   strike);
        Real stdDev = std::sqrt(variance);
        results_.additionalResults["stdDev"] = stdDev;
        Option::Type w = (arguments_.type==VanillaSwap::Payer) ?
                                                Option::Call : Option::Put;
        results_.value = blackFormula(w, strike, atmForward, stdDev, annuity,
                                                                displacement_);

        Time exerciseTime = vol_->timeFromReference(exerciseDate);
        results_.additionalResults["vega"] = std::sqrt(exerciseTime) *
            blackFormulaStdDevDerivative(strike, atmForward, stdDev, annuity,
                                                                displacement_);
    }
void VannaVolgaDoubleBarrierEngine::calculate() const {

    const Real sigmaShift_vega = 0.001;
    const Real sigmaShift_volga = 0.0001;
    const Real spotShift_delta = 0.0001 * spotFX_->value();
    const Real sigmaShift_vanna = 0.0001;

    Handle<Quote> x0Quote(  //used for shift
        boost::make_shared<SimpleQuote>(spotFX_->value()));
    Handle<Quote> atmVolQuote( //used for shift
        boost::make_shared<SimpleQuote>(atmVol_->value()));

    boost::shared_ptr<BlackVolTermStructure> blackVolTS =
        boost::make_shared<BlackConstantVol>(
            Settings::instance().evaluationDate(),
            NullCalendar(), atmVolQuote, Actual365Fixed());
    boost::shared_ptr<BlackScholesMertonProcess> stochProcess =
        boost::make_shared<BlackScholesMertonProcess>(
            x0Quote,
            foreignTS_,
            domesticTS_,
            Handle<BlackVolTermStructure>(blackVolTS));

    boost::shared_ptr<PricingEngine> engineBS =
        boost::make_shared<AnalyticDoubleBarrierEngine>(stochProcess,
                series_);

    BlackDeltaCalculator blackDeltaCalculatorAtm(
        Option::Call, atmVol_->deltaType(), x0Quote->value(),
        domesticTS_->discount(T_), foreignTS_->discount(T_),
        atmVol_->value() * sqrt(T_));
    Real atmStrike = blackDeltaCalculatorAtm.atmStrike(atmVol_->atmType());

    Real call25Vol = vol25Call_->value();
    Real put25Vol = vol25Put_->value();
    BlackDeltaCalculator blackDeltaCalculatorPut25(
        Option::Put, vol25Put_->deltaType(), x0Quote->value(),
        domesticTS_->discount(T_), foreignTS_->discount(T_),
        put25Vol * sqrt(T_));
    Real put25Strike = blackDeltaCalculatorPut25.strikeFromDelta(-0.25);
    BlackDeltaCalculator blackDeltaCalculatorCall25(
        Option::Call, vol25Call_->deltaType(), x0Quote->value(),
        domesticTS_->discount(T_), foreignTS_->discount(T_),
        call25Vol * sqrt(T_));
    Real call25Strike = blackDeltaCalculatorCall25.strikeFromDelta(0.25);

    //here use vanna volga interpolated smile to price vanilla
    std::vector<Real> strikes;
    std::vector<Real> vols;
    strikes.push_back(put25Strike);
    vols.push_back(put25Vol);
    strikes.push_back(atmStrike);
    vols.push_back(atmVol_->value());
    strikes.push_back(call25Strike);
    vols.push_back(call25Vol);
    VannaVolga vannaVolga(x0Quote->value(), foreignTS_->discount(T_), foreignTS_->discount(T_), T_);
    Interpolation interpolation = vannaVolga.interpolate(strikes.begin(), strikes.end(), vols.begin());
    interpolation.enableExtrapolation();
    const boost::shared_ptr<StrikedTypePayoff> payoff =
        boost::dynamic_pointer_cast<StrikedTypePayoff>(arguments_.payoff);
    Real strikeVol = interpolation(payoff->strike());
    //vannila option price
    Real vanillaOption = blackFormula(payoff->optionType(), payoff->strike(),
                                      x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                      strikeVol * sqrt(T_),
                                      domesticTS_->discount(T_));

    //already out
    if((x0Quote->value() > arguments_.barrier[1] || x0Quote->value() < arguments_.barrier[0])
            && arguments_.barrierType[0] == Barrier::DownOut) {
        results_.value = 0.0;
        results_.additionalResults["VanillaPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
        results_.additionalResults["BarrierInPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
        results_.additionalResults["BarrierOutPrice"] = 0.0;
    }
    //already in
    else if((x0Quote->value() > arguments_.barrier[1] || x0Quote->value() < arguments_.barrier[0])
            && arguments_.barrierType[0] == Barrier::DownIn) {
        results_.value = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
        results_.additionalResults["VanillaPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
        results_.additionalResults["BarrierInPrice"] = adaptVanDelta_? bsPriceWithSmile_ : vanillaOption;
        results_.additionalResults["BarrierOutPrice"] = 0.0;
    }
    else {

        //set up BS barrier option pricing
        //only calculate out barrier option price
        // in barrier price = vanilla - out barrier
        boost::shared_ptr<StrikedTypePayoff> payoff
            = boost::static_pointer_cast<StrikedTypePayoff> (arguments_.payoff);
        DoubleBarrierOption doubleBarrierOption(arguments_.barrierType,
                                                arguments_.barrier,
                                                arguments_.rebate,
                                                payoff,
                                                arguments_.exercise);

        doubleBarrierOption.setPricingEngine(engineBS);

        //BS price
        Real priceBS = doubleBarrierOption.NPV();

        Real priceAtmCallBS = blackFormula(Option::Call,atmStrike,
                                           x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                           atmVol_->value() * sqrt(T_),
                                           domesticTS_->discount(T_));
        Real price25CallBS = blackFormula(Option::Call,call25Strike,
                                          x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                          atmVol_->value() * sqrt(T_),
                                          domesticTS_->discount(T_));
        Real price25PutBS = blackFormula(Option::Put,put25Strike,
                                         x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                         atmVol_->value() * sqrt(T_),
                                         domesticTS_->discount(T_));

        //market price
        Real priceAtmCallMkt = blackFormula(Option::Call,atmStrike,
                                            x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                            atmVol_->value() * sqrt(T_),
                                            domesticTS_->discount(T_));
        Real price25CallMkt = blackFormula(Option::Call,call25Strike,
                                           x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                           call25Vol * sqrt(T_),
                                           domesticTS_->discount(T_));
        Real price25PutMkt = blackFormula(Option::Put,put25Strike,
                                          x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_),
                                          put25Vol * sqrt(T_),
                                          domesticTS_->discount(T_));

        //Analytical Black Scholes formula
        NormalDistribution norm;
        Real d1atm = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/atmStrike)
                      + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_));
        Real vegaAtm_Analytical = x0Quote->value() * norm(d1atm) * sqrt(T_) * foreignTS_->discount(T_);
        Real vannaAtm_Analytical = vegaAtm_Analytical/x0Quote->value() *(1.0 - d1atm/(atmVolQuote->value()*sqrt(T_)));
        Real volgaAtm_Analytical = vegaAtm_Analytical * d1atm * (d1atm - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value();

        Real d125call = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/call25Strike)
                         + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_));
        Real vega25Call_Analytical = x0Quote->value() * norm(d125call) * sqrt(T_) * foreignTS_->discount(T_);
        Real vanna25Call_Analytical = vega25Call_Analytical/x0Quote->value() *(1.0 - d125call/(atmVolQuote->value()*sqrt(T_)));
        Real volga25Call_Analytical = vega25Call_Analytical * d125call * (d125call - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value();

        Real d125Put = (std::log(x0Quote->value()* foreignTS_->discount(T_)/ domesticTS_->discount(T_)/put25Strike)
                        + 0.5*std::pow(atmVolQuote->value(),2.0) * T_)/(atmVolQuote->value() * sqrt(T_));
        Real vega25Put_Analytical = x0Quote->value() * norm(d125Put) * sqrt(T_) * foreignTS_->discount(T_);
        Real vanna25Put_Analytical = vega25Put_Analytical/x0Quote->value() *(1.0 - d125Put/(atmVolQuote->value()*sqrt(T_)));
        Real volga25Put_Analytical = vega25Put_Analytical * d125Put * (d125Put - atmVolQuote->value() * sqrt(T_))/atmVolQuote->value();


        //BS vega
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vega);
        doubleBarrierOption.recalculate();
        Real vegaBarBS = (doubleBarrierOption.NPV() - priceBS)/sigmaShift_vega;
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() - sigmaShift_vega);//setback

        //BS volga

        //vegaBar2
        //base NPV
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_volga);
        doubleBarrierOption.recalculate();
        Real priceBS2 = doubleBarrierOption.NPV();

        //shifted npv
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vega);
        doubleBarrierOption.recalculate();
        Real vegaBarBS2 = (doubleBarrierOption.NPV() - priceBS2)/sigmaShift_vega;
        Real volgaBarBS = (vegaBarBS2 - vegaBarBS)/sigmaShift_volga;
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value()
                - sigmaShift_volga
                - sigmaShift_vega);//setback

        //BS Delta
        //base delta
        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//shift forth
        doubleBarrierOption.recalculate();
        Real priceBS_delta1 = doubleBarrierOption.NPV();

        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() - 2 * spotShift_delta);//shift back
        doubleBarrierOption.recalculate();
        Real priceBS_delta2 = doubleBarrierOption.NPV();

        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() +  spotShift_delta);//set back
        Real deltaBar1 = (priceBS_delta1 - priceBS_delta2)/(2.0*spotShift_delta);

        //shifted vanna
        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() + sigmaShift_vanna);//shift sigma
        //shifted delta
        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() + spotShift_delta);//shift forth
        doubleBarrierOption.recalculate();
        priceBS_delta1 = doubleBarrierOption.NPV();

        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() - 2 * spotShift_delta);//shift back
        doubleBarrierOption.recalculate();
        priceBS_delta2 = doubleBarrierOption.NPV();

        boost::static_pointer_cast<SimpleQuote> (x0Quote.currentLink())->setValue(x0Quote->value() +  spotShift_delta);//set back
        Real deltaBar2 = (priceBS_delta1 - priceBS_delta2)/(2.0*spotShift_delta);

        Real vannaBarBS = (deltaBar2 - deltaBar1)/sigmaShift_vanna;

        boost::static_pointer_cast<SimpleQuote> (atmVolQuote.currentLink())->setValue(atmVolQuote->value() - sigmaShift_vanna);//set back

        //Matrix
        Matrix A(3,3,0.0);

        //analytical
        A[0][0] = vegaAtm_Analytical;
        A[0][1] = vega25Call_Analytical;
        A[0][2] = vega25Put_Analytical;
        A[1][0] = vannaAtm_Analytical;
        A[1][1] = vanna25Call_Analytical;
        A[1][2] = vanna25Put_Analytical;
        A[2][0] = volgaAtm_Analytical;
        A[2][1] = volga25Call_Analytical;
        A[2][2] = volga25Put_Analytical;

        Array b(3,0.0);
        b[0] = vegaBarBS;
        b[1] = vannaBarBS;
        b[2] = volgaBarBS;
        Array q = inverse(A) * b;

        Real H = arguments_.barrier[1];
        Real L = arguments_.barrier[0];
        Real theta_tilt_minus = ((domesticTS_->zeroRate(T_, Continuous) - foreignTS_->zeroRate(T_, Continuous))/atmVol_->value() - atmVol_->value()/2.0)*std::sqrt(T_);
        Real h = 1.0/atmVol_->value() * std::log(H/x0Quote->value())/std::sqrt(T_);
        Real l = 1.0/atmVol_->value() * std::log(L/x0Quote->value())/std::sqrt(T_);
        CumulativeNormalDistribution cnd;

        Real doubleNoTouch = 0.0;
        for(int j = -series_; j< series_; j++ ) {
            Real e_minus = 2*j*(h-l) - theta_tilt_minus;
            doubleNoTouch += std::exp(-2.0*j*theta_tilt_minus*(h-l))*(cnd(h+e_minus) - cnd(l+e_minus))
                             - std::exp(-2.0*j*theta_tilt_minus*(h-l)+2.0*theta_tilt_minus*h)*(cnd(h-2.0*h+e_minus) - cnd(l-2.0*h+e_minus));
        }

        Real p_survival = doubleNoTouch;

        Real lambda = p_survival ;
        Real adjust = q[0]*(priceAtmCallMkt - priceAtmCallBS)
                      + q[1]*(price25CallMkt - price25CallBS)
                      + q[2]*(price25PutMkt - price25PutBS);
        Real outPrice = priceBS + lambda*adjust;//
        Real inPrice;

        //adapt Vanilla delta
        if(adaptVanDelta_ == true) {
            outPrice += lambda*(bsPriceWithSmile_ - vanillaOption);
            //capfloored by (0, vanilla)
            outPrice = std::max(0.0, std::min(bsPriceWithSmile_, outPrice));
            inPrice = bsPriceWithSmile_ - outPrice;
        }
        else {
            //capfloored by (0, vanilla)
            outPrice = std::max(0.0, std::min(vanillaOption , outPrice));
            inPrice = vanillaOption - outPrice;
        }

        if(arguments_.barrierType[0] == Barrier::DownOut)
            results_.value = outPrice;
        else
            results_.value = inPrice;
        results_.additionalResults["VanillaPrice"] = vanillaOption;
        results_.additionalResults["BarrierInPrice"] = inPrice;
        results_.additionalResults["BarrierOutPrice"] = outPrice;
        results_.additionalResults["lambda"] = lambda;
    }
}
예제 #19
0
    void KahaleSmileSection::compute() {

        std::pair<Size, Size> afIdx = ssutils_->arbitragefreeIndices();
        leftIndex_ = afIdx.first;
        rightIndex_ = afIdx.second;

        cFunctions_ = std::vector<boost::shared_ptr<cFunction> >(
            rightIndex_ - leftIndex_ + 2);

        // extrapolation in the leftmost interval

        Brent brent;
        bool success;
        Real secl = 0.0;

        do {
            success = true;
            try {
                Real k1 = k_[leftIndex_];
                Real c1 = c_[leftIndex_];
                Real c0 = c_[0];
                secl = (c_[leftIndex_] - c_[0]) / (k_[leftIndex_] - k_[0]);
                Real sec = (c_[leftIndex_ + 1] - c_[leftIndex_]) /
                           (k_[leftIndex_ + 1] - k_[leftIndex_]);
                Real c1p;
                if (interpolate_)
                    c1p = (secl + sec) / 2;
                else {
                    c1p = (blackFormula(Option::Call, k1 + gap_, f_,
                                        sqrt(source_->variance(k1 + gap_))) -
                           blackFormula(Option::Call, k1, f_,
                                        sqrt(source_->variance(k1)))) /
                          gap_;
                    QL_REQUIRE(secl < c1p && c1p <= 0.0, "dummy");
                    // can not extrapolate so throw exception which is caught
                    // below
                }
                sHelper1 sh1(k1, c0, c1, c1p);
                Real s = brent.solve(sh1, QL_KAHALE_ACC, 0.20, 0.00,
                                     QL_KAHALE_SMAX); // numerical parameters
                                                      // hardcoded here
                sh1(s);
                boost::shared_ptr<cFunction> cFct1(
                    new cFunction(sh1.f_, s, 0.0, sh1.b_));
                cFunctions_[0] = cFct1;
                // sanity check - in rare cases we can get digitials
                // which are not monotonic or greater than 1.0
                // due to numerical effects. Move to the next index in
                // these cases.
                Real dig = digitalOptionPrice(k1 / 2.0);
                QL_REQUIRE(dig >= -c1p && dig <= 1.0, "dummy");
            }
            catch (...) {
                leftIndex_++;
                success = false;
            }
        } while (!success && leftIndex_ < rightIndex_);

        QL_REQUIRE(
            leftIndex_ < rightIndex_,
            "can not extrapolate to left, right index of af region reached ("
                << rightIndex_ << ")");

        // interpolation

        Real cp0 = 0.0, cp1 = 0.0;

        if (interpolate_) {

            for (Size i = leftIndex_; i < rightIndex_; i++) {
                Real k0 = k_[i];
                Real k1 = k_[i + 1];
                Real c0 = c_[i];
                Real c1 = c_[i + 1];
                Real sec = (c_[i + 1] - c_[i]) / (k_[i + 1] - k_[i]);
                if (i == leftIndex_)
                    cp0 = leftIndex_ > 0 ? (secl + sec) / 2.0 : sec;
                Real secr;
                if (i == rightIndex_ - 1)
                    secr = 0.0;
                else
                    secr = (c_[i + 2] - c_[i + 1]) / (k_[i + 2] - k_[i + 1]);
                cp1 = (sec + secr) / 2.0;
                aHelper ah(k0, k1, c0, c1, cp0, cp1);
                Real a;
                bool valid = false;
                try {
                    a = brent.solve(
                        ah, QL_KAHALE_ACC, 0.5 * (cp1 + (1.0 + cp0)),
                        cp1 + QL_KAHALE_EPS, 1.0 + cp0 - QL_KAHALE_EPS);
                    // numerical parameters hardcoded here
                    valid = true;
                }
                catch (...) {
                    // delete the right point of the interval where we try to
                    // interpolate
                    moneynessGrid_.erase(moneynessGrid_.begin() + (i + 1));
                    k_.erase(k_.begin() + (i + 1));
                    c_.erase(c_.begin() + (i + 1));
                    cFunctions_.erase(cFunctions_.begin() + (i + 1));
                    rightIndex_--;
                    i--;
                }
                if (valid) {
                    ah(a);
                    boost::shared_ptr<cFunction> cFct(
                        new cFunction(ah.f_, ah.s_, a, ah.b_));
                    cFunctions_[leftIndex_ > 0 ? i - leftIndex_ + 1 : 0] = cFct;
                    cp0 = cp1;
                }
            }
        }

        // extrapolation of right wing

        do {
            success = true;
            try {
                Real k0 = k_[rightIndex_];
                Real c0 = c_[rightIndex_];
                Real cp0;
                if (interpolate_)
                    cp0 = 0.5 * (c_[rightIndex_] - c_[rightIndex_ - 1]) /
                          (k_[rightIndex_] - k_[rightIndex_ - 1]);
                else {
                    cp0 = (blackFormula(Option::Call, k0, f_,
                                        sqrt(source_->variance(k0))) -
                           blackFormula(Option::Call, k0 - gap_, f_,
                                        sqrt(source_->variance(k0 - gap_)))) /
                          gap_;
                }
                boost::shared_ptr<cFunction> cFct;
                if (exponentialExtrapolation_) {
                    QL_REQUIRE(-cp0 / c0 > 0.0, "dummy"); // this is caught
                                                          // below
                    cFct = boost::shared_ptr<cFunction>(
                        new cFunction(-cp0 / c0, std::log(c0) - cp0 / c0 * k0));
                } else {
                    sHelper sh(k0, c0, cp0);
                    Real s;
                    s = brent.solve(sh, QL_KAHALE_ACC, 0.20, 0.0,
                                    QL_KAHALE_SMAX); // numerical parameters
                                                     // hardcoded here
                    sh(s);
                    cFct = boost::shared_ptr<cFunction>(
                        new cFunction(sh.f_, s, 0.0, 0.0));
                }
                cFunctions_[rightIndex_ - leftIndex_ + 1] = cFct;
            }
            catch (...) {
                rightIndex_--;
                success = false;
            }
        } while (!success && rightIndex_ > leftIndex_);

        QL_REQUIRE(
            leftIndex_ < rightIndex_,
            "can not extrapolate to right, left index of af region reached ("
                << leftIndex_ << ")");
    }