public double ShortEndTargetFunction(double guess) { DateTime baseDate = _baseCurve.GetBaseDate(); DateTime date0 = _assetDates.First(); DateTime date1 = _assetDates.Last(); double d0 = _baseCurve.GetDiscountFactor(date0); double d1 = _baseCurve.GetDiscountFactor(date1); double y = _dayCounter.YearFraction(date0, date1); double y0 = _dayCounter.YearFraction(baseDate, date0); double y1 = _dayCounter.YearFraction(baseDate, date1); double z0 = RateAnalytics.DiscountFactorToZeroRate(d0, y0, _compoundingPeriod); double z1 = RateAnalytics.DiscountFactorToZeroRate(d1, y1, _compoundingPeriod); double projectedRate = 1 / y * (d0 / d1 - 1); double basisSpread = (double)_asset.MarketQuote.value; double term1 = Math.Pow(1 + 0.25 * (z0 + guess), -4 * y0); double term2 = Math.Pow(1 + 0.25 * (z1 + guess), -4 * y1); double term3 = y * term2 * (projectedRate + basisSpread); double result = -term1 + term2 + term3; return(result); }
/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="baseZeroCurve">The base Zero Curve</param> /// <param name="algorithmHolder">The algorithmHolder</param> /// <returns></returns> public TermPoint[] Bootstrap(List <IPriceableRateAssetController> priceableAssets, IRateCurve baseZeroCurve, PricingStructureAlgorithmsHolder algorithmHolder) { var items = new SortedDictionary <DateTime, Pair <string, decimal> >(); DateTime baseDate = baseZeroCurve.GetBaseDate(); items.Add(baseDate, new Pair <string, decimal>(null, 1m)); bool firstTime = true; const double compoundingPeriod = 0.25; IPriceableRateAssetController previousAsset = null; IEnumerable <TermPoint> basePillars = baseZeroCurve.GetTermCurve().point; IDayCounter dayCounter = null; foreach (IPriceableRateAssetController priceableAsset in priceableAssets) { List <DateTime> assetDates; if (priceableAsset is PriceableSwapRateAsset swap) { dayCounter = DayCounterHelper.Parse(swap.DayCountFraction.Value); assetDates = swap.AdjustedPeriodDates; } else { PriceableDeposit deposit = priceableAsset as PriceableDeposit; if (deposit == null) { throw new ArgumentException( $"PriceableAsset must be a PriceableSwapRateAsset or PriceableDeposit, '{priceableAsset.GetType()}' is not implemented."); } dayCounter = DayCounterHelper.Parse(deposit.Deposit.dayCountFraction.Value); assetDates = new List <DateTime> { deposit.AdjustedStartDate, deposit.GetRiskMaturityDate() }; } DateTime maturityDate = priceableAsset.GetRiskMaturityDate(); if (items.Keys.Contains(maturityDate)) { throw new ArgumentException( $"Duplicate priceable asset on '{maturityDate:yyyy-MM-dd}'", nameof(priceableAssets)); } //The min and max values to use with the solver. const int xmin = -1; const int xmax = 1; var accuracy = 10 ^ -12; if (firstTime) { firstTime = false; // Solve to find the quarterly compounded zero rate spread var solverFunctions = new NewtonRaphsonSolverFunctions(priceableAsset, null, algorithmHolder, baseZeroCurve, baseDate, items, compoundingPeriod, ZeroRateSpreads, dayCounter, assetDates); var solver = new Newton(); var initialGuess = (double)priceableAsset.MarketQuote.value; //var solution = new CenteredFiniteDifferenceDerivative(); Func <double, double> f = solverFunctions.ShortEndTargetFunction; var derivativeOfTargetFunction = new NumericalDerivative().CreateDerivativeFunctionHandle(f, 1); double zeroRateSpread = solver.Solve(f, derivativeOfTargetFunction, accuracy, initialGuess, xmin, xmax); // add first point DateTime startDate = assetDates.First(); decimal df = (decimal)GetAdjustedDiscountFactor(baseDate, startDate, dayCounter, zeroRateSpread, baseZeroCurve); if (startDate != baseDate) { items.Add(startDate, new Pair <string, decimal>(null, df)); } ZeroRateSpreads.Add(maturityDate, zeroRateSpread); // add extra points IEnumerable <TermPoint> extraPoints = basePillars.Where(b => (DateTime)b.term.Items[0] > startDate && (DateTime)b.term.Items[0] < maturityDate); // Extrapolate the preceding extra points foreach (TermPoint extraPoint in extraPoints) { DateTime date = (DateTime)extraPoint.term.Items[0]; df = (decimal)GetAdjustedDiscountFactor(baseDate, date, dayCounter, zeroRateSpread, baseZeroCurve); items.Add(date, new Pair <string, decimal>(extraPoint.id, df)); } // add final point df = (decimal)GetAdjustedDiscountFactor(baseDate, maturityDate, dayCounter, zeroRateSpread, baseZeroCurve); items.Add(maturityDate, new Pair <string, decimal>(priceableAsset.Id, df)); } else { items.Add(maturityDate, new Pair <string, decimal>(priceableAsset.Id, 0)); ZeroRateSpreads.Add(maturityDate, (double)priceableAsset.MarketQuote.value); // Solve to find the quarterly compounded zero rate spread var solverFunctions = new NewtonRaphsonSolverFunctions(priceableAsset, previousAsset, algorithmHolder, baseZeroCurve, baseDate, items, compoundingPeriod, ZeroRateSpreads, dayCounter, assetDates); Func <double, double> f = solverFunctions.LongEndTargetFunction; var solver = new Newton(); var initialGuess = (double)priceableAsset.MarketQuote.value; var derivativeOfTargetFunction = new NumericalDerivative().CreateDerivativeFunctionHandle(f, 1); double zeroRateSpread = solver.Solve(f, derivativeOfTargetFunction, accuracy, initialGuess, xmin, xmax); // Update discount factor value decimal df = (decimal)GetAdjustedDiscountFactor(baseDate, maturityDate, dayCounter, zeroRateSpread, baseZeroCurve); items[maturityDate].Second = df; ZeroRateSpreads[maturityDate] = zeroRateSpread; solverFunctions.UpdateDiscountFactors(baseDate); } previousAsset = priceableAsset; } // Extrapolate the following extra points IEnumerable <TermPoint> finalPoints = basePillars.Where(b => (DateTime)b.term.Items[0] > items.Last().Key); KeyValuePair <DateTime, double> zeroRateSpreadFinal = ZeroRateSpreads.Last(); foreach (TermPoint extraPoint in finalPoints) { DateTime date = (DateTime)extraPoint.term.Items[0]; decimal df = (decimal)GetAdjustedDiscountFactor(baseDate, date, dayCounter, zeroRateSpreadFinal.Value, baseZeroCurve); items.Add(date, new Pair <string, decimal>(extraPoint.id, df)); } return(TermPointsFactory.Create(items)); }