// 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); }
/*! 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); }
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); }
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; } }
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" }
//! 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); }
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); }
/// <summary> /// not applicable here /// </summary> public override double capletPrice(double d) { Utils.QL_FAIL("not available"); return(0); }
/// <summary> /// not applicable here /// </summary> public override double floorletRate(double d) { Utils.QL_FAIL("not available"); return(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); }
/// <summary> /// not applicable here /// </summary> public override double convexityAdjustment() { Utils.QL_FAIL("not defined for average-BMA coupon"); return(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); }
/* 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; } }
// 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); }
public override double secondDerivative(double d) { Utils.QL_FAIL("Second derivative calculation not implemented for kernel interpolation"); return(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); }
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; } }
/// <summary> /// not applicable here; use indexFixings() instead /// </summary> public override double indexFixing() { Utils.QL_FAIL("no single fixing for average-BMA coupon"); return(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_)); }
/// <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; } }
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; } }
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); }
/*! 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); }
// 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); }
/*! 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); }
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); }
public override double primitive(double d) { Utils.QL_FAIL("Primitive calculation not implemented for kernel interpolation"); return(0); }
public void visit(Payoff p) { Utils.QL_FAIL("unsupported payoff type: " + p.name()); }
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; }