public override Lattice tree(TimeGrid grid) { TermStructureFittingParameter phi = new TermStructureFittingParameter(termStructure()); ShortRateDynamics numericDynamics = new Dynamics(phi, a(), sigma()); TrinomialTree trinomial = new TrinomialTree(numericDynamics.process(), grid); ShortRateTree numericTree = new ShortRateTree(trinomial, numericDynamics, grid); TermStructureFittingParameter.NumericalImpl impl = (TermStructureFittingParameter.NumericalImpl)phi.implementation(); impl.reset(); double value = 1.0; double vMin = -50.0; double vMax = 50.0; for (int i = 0; i < (grid.size() - 1); i++) { double discountBond = termStructure().link.discount(grid[i + 1]); double xMin = trinomial.underlying(i, 0); double dx = trinomial.dx(i); Helper finder = new Helper(i, xMin, dx, discountBond, numericTree); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); value = s1d.solve(finder, 1e-7, value, vMin, vMax); impl.setvalue(grid[i], value); } return(numericTree); }
/// <summary> /// Calculate the Option Adjusted Spread (OAS) /// <remarks> /// Calculates the spread that needs to be added to the the /// reference curve so that the theoretical model value /// matches the marketPrice. /// </remarks> /// </summary> /// <param name="cleanPrice"></param> /// <param name="engineTS"></param> /// <param name="dayCounter"></param> /// <param name="compounding"></param> /// <param name="frequency"></param> /// <param name="settlement"></param> /// <param name="accuracy"></param> /// <param name="maxIterations"></param> /// <param name="guess"></param> /// <returns></returns> public double OAS(double cleanPrice, Handle <YieldTermStructure> engineTS, DayCounter dayCounter, Compounding compounding, Frequency frequency, Date settlement = null, double accuracy = 1.0e-10, int maxIterations = 100, double guess = 0.0) { if (settlement == null) { settlement = settlementDate(); } double dirtyPrice = cleanPrice + accruedAmount(settlement); var f = new NpvSpreadHelper(this); OasHelper obj = new OasHelper(f, dirtyPrice); Brent solver = new Brent(); solver.setMaxEvaluations(maxIterations); double step = 0.001; double oas = solver.solve(obj, accuracy, guess, step); return(continuousToConv(oas, this, engineTS, dayCounter, compounding, frequency)); }
private double strikeFromPrice(double price, Option.Type optionType, double referenceStrike) { double a, b, min, max, k; if (optionType == Option.Type.Call) { a = swapRateValue_; min = referenceStrike; b = max = k = Math.Min(smileSection_.maxStrike(), shiftedUpperBound_); } else { a = min = k = Math.Max(smileSection_.minStrike(), shiftedLowerBound_); b = swapRateValue_; max = referenceStrike; } PriceHelper h = new PriceHelper(smileSection_, optionType, price); Brent solver = new Brent(); try { k = solver.solve(h, 1.0E-5, swapRateValue_, a, b); } catch (Exception) { // use default value set above } return(Math.Min(Math.Max(k, min), max)); }
//! Black volatility implied by the model public double impliedVolatility(double targetValue, double accuracy, int maxEvaluations, double minVol, double maxVol) { ImpliedVolatilityHelper f = new ImpliedVolatilityHelper(this, targetValue); Brent solver = new Brent(); solver.setMaxEvaluations(maxEvaluations); return(solver.solve(f, accuracy, volatility_.link.value(), minVol, maxVol)); }
public double MonthlyYield() { Brent solver = new Brent(); solver.setMaxEvaluations(100); List <CashFlow> cf = expectedCashflows(); MonthlyYieldFinder objective = new MonthlyYieldFinder(notional(settlementDate()), cf, settlementDate()); return(solver.solve(objective, 1.0e-10, 0.02, 0.0, 1.0) / 100); }
public static double calculate(Instrument instrument, IPricingEngine engine, SimpleQuote volQuote, double targetValue, double accuracy, int maxEvaluations, double minVol, double maxVol) { instrument.setupArguments(engine.getArguments()); engine.getArguments().validate(); PriceError f = new PriceError(engine, volQuote, targetValue); Brent solver = new Brent(); solver.setMaxEvaluations(maxEvaluations); double guess = (minVol + maxVol) / 2.0; double result = solver.solve(f, accuracy, guess, minVol, maxVol); return(result); }
private List <double> spreadsVolImplied() { Brent solver = new Brent(); List <double> result = new InitializedList <double>(nOptionExpiries_, 0.0); double guess = 0.0001, minSpread = -0.1, maxSpread = 0.1; for (int j = 0; j < nOptionExpiries_; ++j) { ObjectiveFunction f = new ObjectiveFunction(stripper1_, caps_[j], atmCapFloorPrices_[j]); solver.setMaxEvaluations(maxEvaluations_); double root = solver.solve(f, accuracy_, guess, minSpread, maxSpread); result[j] = root; } return(result); }
protected override double blackVolImpl(double t, double strike) { HestonProcess process = hestonModel_.link.process(); double df = process.riskFreeRate().link.discount(t, true); double div = process.dividendYield().link.discount(t, true); double spotPrice = process.s0().link.value(); double fwd = spotPrice * process.dividendYield().link.discount(t, true) / process.riskFreeRate().link.discount(t, true); var payoff = new PlainVanillaPayoff(fwd > strike ? Option.Type.Put : Option.Type.Call, strike); double kappa = hestonModel_.link.kappa(); double theta = hestonModel_.link.theta(); double rho = hestonModel_.link.rho(); double sigma = hestonModel_.link.sigma(); double v0 = hestonModel_.link.v0(); AnalyticHestonEngine.ComplexLogFormula cpxLogFormula = AnalyticHestonEngine.ComplexLogFormula.Gatheral; AnalyticHestonEngine hestonEnginePtr = null; double?npv = null; int evaluations = 0; AnalyticHestonEngine.doCalculation( df, div, spotPrice, strike, t, kappa, theta, sigma, v0, rho, payoff, integration_, cpxLogFormula, hestonEnginePtr, ref npv, ref evaluations); if (npv <= 0.0) { return(Math.Sqrt(theta)); } Brent solver = new Brent(); solver.setMaxEvaluations(10000); double guess = Math.Sqrt(theta); double accuracy = Const.QL_EPSILON; var f = new ImpliedVolHelper(payoff.optionType(), strike, fwd, t, df, npv.Value); return(solver.solve(f, accuracy, guess, 0.01)); }
/// <summary> /// Returns the Black implied forward yield volatility /// <remarks> /// the forward yield volatility, see Hull, Fourth Edition, /// Chapter 20, pg 536). Relevant only to European put/call /// schedules /// </remarks> /// </summary> /// <param name="targetValue"></param> /// <param name="discountCurve"></param> /// <param name="accuracy"></param> /// <param name="maxEvaluations"></param> /// <param name="minVol"></param> /// <param name="maxVol"></param> /// <returns></returns> public double impliedVolatility(double targetValue, Handle <YieldTermStructure> discountCurve, double accuracy, int maxEvaluations, double minVol, double maxVol) { calculate(); Utils.QL_REQUIRE(!isExpired(), () => "instrument expired"); double guess = 0.5 * (minVol + maxVol); blackDiscountCurve_.linkTo(discountCurve, false); ImpliedVolHelper f = new ImpliedVolHelper(this, targetValue); Brent solver = new Brent(); solver.setMaxEvaluations(maxEvaluations); return(solver.solve(f, accuracy, guess, minVol, maxVol)); }
public double value(double x) { // first find the right side of the interval double upper = guess_; int evaluations = maxEvaluations_; while (nonCentralDist_.value(upper) < x && evaluations > 0) { upper *= 2.0; --evaluations; } // use a brent solver for the rest Brent solver = new Brent(); solver.setMaxEvaluations(evaluations); return(solver.solve(new IncChiQuareFinder(x, nonCentralDist_.value), accuracy_, 0.75 * upper, (evaluations == maxEvaluations_) ? 0.0 : 0.5 * upper, upper)); }
public double value(double x) { CumulativeNormalDistribution phi = new CumulativeNormalDistribution(); double temp = (x - mux_) / sigmax_; double txy = Math.Sqrt(1.0 - rhoxy_ * rhoxy_); Vector lambda = new Vector(size_); int i; for (i = 0; i < size_; i++) { double tau = (i == 0 ? t_[0] - T_ : t_[i] - t_[i - 1]); double c = (i == size_ - 1 ? (1.0 + rate_ * tau) : rate_ * tau); lambda[i] = c * A_[i] * Math.Exp(-Ba_[i] * x); } SolvingFunction function = new SolvingFunction(lambda, Bb_); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); double yb = s1d.solve(function, 1e-6, 0.00, -100.0, 100.0); double h1 = (yb - muy_) / (sigmay_ * txy) - rhoxy_ * (x - mux_) / (sigmax_ * txy); double value = phi.value(-w_ * h1); for (i = 0; i < size_; i++) { double h2 = h1 + Bb_[i] * sigmay_ *Math.Sqrt(1.0 - rhoxy_ *rhoxy_); double kappa = -Bb_[i] * (muy_ - 0.5 * txy * txy * sigmay_ * sigmay_ * Bb_[i] + rhoxy_ * sigmay_ * (x - mux_) / sigmax_); value -= lambda[i] * Math.Exp(kappa) * phi.value(-w_ * h2); } return(Math.Exp(-0.5 * temp * temp) * value / (sigmax_ * Math.Sqrt(2.0 * QLCore.Const.M_PI))); }
//! Tree build-up + numerical fitting to term-structure public ShortRateTree(TrinomialTree tree, ShortRateDynamics dynamics, TermStructureFittingParameter.NumericalImpl theta, TimeGrid timeGrid) : base(timeGrid, tree.size(1)) { tree_ = tree; dynamics_ = dynamics; theta.reset(); double value = 1.0; double vMin = -100.0; double vMax = 100.0; for (int i = 0; i < (timeGrid.size() - 1); i++) { double discountBond = theta.termStructure().link.discount(t_[i + 1]); Helper finder = new Helper(i, discountBond, theta, this); Brent s1d = new Brent(); s1d.setMaxEvaluations(1000); value = s1d.solve(finder, 1e-7, value, vMin, vMax); theta.change(value); } }
//! implied Z-spread. public static double zSpread(Leg leg, double npv, YieldTermStructure discount, DayCounter dayCounter, Compounding compounding, Frequency frequency, bool includeSettlementDateFlows, Date settlementDate = null, Date npvDate = null, double accuracy = 1.0e-10, int maxIterations = 100, double guess = 0.0) { if (settlementDate == null) { settlementDate = Settings.Instance.evaluationDate(); } if (npvDate == null) { npvDate = settlementDate; } Brent solver = new Brent(); solver.setMaxEvaluations(maxIterations); ZSpreadFinder objFunction = new ZSpreadFinder(leg, discount, npv, dayCounter, compounding, frequency, includeSettlementDateFlows, settlementDate, npvDate); double step = 0.01; return(solver.solve(objFunction, accuracy, guess, step)); }
// 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); }
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 = accuracy_ != null ? accuracy_.Value : 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_ = new List <double>(ts_.data_); List <double?> minValues = new InitializedList <double?>(alive_ + 1, null); List <double?> maxValues = new InitializedList <double?>(alive_ + 1, null); List <int> attempts = new InitializedList <int>(alive_ + 1, 1); for (int i = 1; i <= alive_; ++i) { // shorter aliases for readability and to avoid duplication double?min = minValues[i]; double?max = maxValues[i]; // bracket root and calculate guess if (min == null) { min = (minValue_ != null ? minValue_ : ts_.minValueAfter(i, ts_, validData, firstAliveHelper_)); max = (maxValue_ != null ? maxValue_ : ts_.maxValueAfter(i, ts_, validData, firstAliveHelper_)); } else { min = (min.Value < 0.0 ? min.Value * minFactor_ : min.Value / minFactor_); max = (max.Value > 0.0 ? max.Value * maxFactor_ : max.Value / maxFactor_); } double guess = ts_.guess(i, ts_, validData, firstAliveHelper_); // adjust guess if needed if (guess >= max.Value) { guess = max.Value - (max.Value - min.Value) / 5.0; } else if (guess <= min.Value) { guess = min.Value + (max.Value - min.Value) / 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_); } 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_.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.Value, max.Value); } else { ts_.data_[i] = firstSolver_.solve(error, accuracy, guess, min.Value, max.Value); } } catch (Exception e) { if (validCurve_) { // the previous curve state might have been a // bad guess, so we retry without using it. // This would be tricky to do here (we're // inside multiple nested for loops, we need // to re-initialize...), so we invalidate the // curve, make a recursive call and then exit. validCurve_ = initialized_ = false; calculate(); return; } // If we have more attempts left on this iteration, try again. Note that the max and min // bounds will be widened on the retry. if (attempts[i] < maxAttempts_) { attempts[i]++; i--; continue; } if (dontThrow_) { // Use the fallback value var error = new BootstrapError <T, U>(ts_, ts_.instruments_[i - 1], i); ts_.data_[i] = dontThrowFallback(error, min.Value, max.Value, dontThrowSteps_); // Remember to update the interpolation. If we don't and we are on the last "i", we will still // have the last attempted value in the solver being used in ts_->interpolation_. ts_.interpolation_.update(); } else { Utils.QL_FAIL((iteration + 1) + " iteration: failed " + "at " + (i) + " alive instrument, " + "pillar " + ts_.instruments_[i - 1].pillarDate() + ", " + "maturity " + ts_.instruments_[i - 1].maturityDate() + ", 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; }
public override void calculate() { Utils.QL_REQUIRE(arguments_.settlementMethod != Settlement.Method.ParYieldCurve, () => "cash-settled (ParYieldCurve) swaptions not priced by Jamshidian engine"); Utils.QL_REQUIRE(arguments_.exercise.type() == Exercise.Type.European, () => "cannot use the Jamshidian decomposition on exotic swaptions"); Utils.QL_REQUIRE(arguments_.swap.spread.IsEqual(0.0), () => "non zero spread (" + arguments_.swap.spread + ") not allowed"); Date referenceDate; DayCounter dayCounter; ITermStructureConsistentModel tsmodel = (ITermStructureConsistentModel)base.model_.link; try { if (tsmodel != null) { referenceDate = tsmodel.termStructure().link.referenceDate(); dayCounter = tsmodel.termStructure().link.dayCounter(); } else { referenceDate = termStructure_.link.referenceDate(); dayCounter = termStructure_.link.dayCounter(); } } catch { referenceDate = termStructure_.link.referenceDate(); dayCounter = termStructure_.link.dayCounter(); } List <double> amounts = new InitializedList <double>(arguments_.fixedCoupons.Count); for (int i = 0; i < amounts.Count; i++) { amounts[i] = arguments_.fixedCoupons[i]; } amounts[amounts.Count - 1] = amounts.Last() + arguments_.nominal; double maturity = dayCounter.yearFraction(referenceDate, arguments_.exercise.date(0)); List <double> fixedPayTimes = new InitializedList <double>(arguments_.fixedPayDates.Count); for (int i = 0; i < fixedPayTimes.Count; i++) { fixedPayTimes[i] = dayCounter.yearFraction(referenceDate, arguments_.fixedPayDates[i]); } rStarFinder finder = new rStarFinder(model_, arguments_.nominal, maturity, fixedPayTimes, amounts); Brent s1d = new Brent(); double minStrike = -10.0; double maxStrike = 10.0; s1d.setMaxEvaluations(10000); s1d.setLowerBound(minStrike); s1d.setUpperBound(maxStrike); double rStar = s1d.solve(finder, 1e-8, 0.05, minStrike, maxStrike); Option.Type w = arguments_.type == VanillaSwap.Type.Payer ? Option.Type.Put : Option.Type.Call; int size = arguments_.fixedCoupons.Count; double value = 0.0; for (int i = 0; i < size; i++) { double fixedPayTime = dayCounter.yearFraction(referenceDate, arguments_.fixedPayDates[i]); double strike = model_.link.discountBond(maturity, fixedPayTime, rStar); double dboValue = model_.link.discountBondOption( w, strike, maturity, fixedPayTime); value += amounts[i] * dboValue; } results_.value = value; }