/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="referenceCurve">The reference curve.</param> /// <param name="baseDate">The base date.</param> /// <param name="termCurve">The term Curve with pre-existing points. This will not work if there are no points.</param> /// <param name="tolerance">Solver tolerance to use.</param> /// <returns></returns> public static TermPoint[] Bootstrap(IList <IPriceableRateSpreadAssetController> priceableAssets, IRateCurve referenceCurve, DateTime baseDate, TermCurve termCurve, Double tolerance) { const double min = 0.000000001; const double max = 1; Dictionary <DateTime, Pair <string, decimal> > items = new Dictionary <DateTime, Pair <string, decimal> > (); // Add the elements (date : discount factor) to the list IDictionary <DateTime, double> dfs = new Dictionary <DateTime, double>(); foreach (var point in termCurve.point) { dfs.Add((DateTime)point.term.Items[0], (double)point.mid); items.Add((DateTime)point.term.Items[0], new Pair <string, decimal>(point.id, point.mid)); } var solver = new Brent(); foreach (var priceableAsset in priceableAssets) { var assetMaturityDate = priceableAsset.GetRiskMaturityDate(); if (dfs.ContainsKey(assetMaturityDate)) { continue; } //The first guess, which should be correct for all priceable assets with analytical solutions that have been implemented. //So far this is only wrt Depos and Futures...This now should automatically extrapolate the required discount factor on a flat rate basis. dfs.Add(assetMaturityDate, (double)priceableAsset.CalculateDiscountFactorAtMaturity(referenceCurve)); var objectiveFunction = new RateSpreadAssetQuote(priceableAsset, referenceCurve, baseDate, termCurve.extrapolationPermitted, dfs, tolerance); // check accuracy so that solver is only called if outside the tolerance. if (!objectiveFunction.InitialValue()) { dfs[assetMaturityDate] = solver.Solve(objectiveFunction, tolerance, dfs[assetMaturityDate], min, max); } items.Add(assetMaturityDate, new Pair <string, decimal>(priceableAsset.Id, (decimal)dfs[assetMaturityDate])); } return(TermPointsFactory.Create(items)); }
/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="referenceCurve">The reference curve.</param> /// <param name="baseDate">The base date.</param> /// <param name="extrapolationPermitted">The extrapolationPermitted flag.</param> /// <param name="tolerance">The tolerance for the solver</param> /// <returns></returns> public static TermPoint[] Bootstrap(IEnumerable <IPriceableRateSpreadAssetController> priceableAssets, IRateCurve referenceCurve, DateTime baseDate, bool extrapolationPermitted, double tolerance) { const double defaultGuess = 0.9; const double min = 0.000000001; const double max = 2; //only works for linear on zero. InterpolationMethod interp = InterpolationMethodHelper.Parse("LogLinearInterpolation"); // Add the first element (date : discount factor) to the list var points = new Dictionary <DateTime, double> { { baseDate, 1d } }; var items = new Dictionary <DateTime, Pair <string, decimal> > { { baseDate, new Pair <string, decimal>("", 1m) } }; var solver = new Brent(); bool first = true; foreach (var priceableAsset in priceableAssets) { DateTime assetMaturityDate = priceableAsset.GetRiskMaturityDate(); if (points.Keys.Contains(assetMaturityDate)) { continue; } if (assetMaturityDate < points.Keys.Last()) { throw new InvalidOperationException("The maturity dates of the assets must be consecutive order"); } if (first) { first = false; // Add the first point points.Add(assetMaturityDate, defaultGuess); IRateCurve curve = IsSpreadAsset(priceableAsset) ? referenceCurve : new SimpleDiscountFactorCurve(baseDate, interp, extrapolationPermitted, points); points[assetMaturityDate] = (double)priceableAsset.CalculateDiscountFactorAtMaturity(curve); } else { //This now should automatically extrapolate the required discount factor on a flat rate basis. IRateCurve curve = IsSpreadAsset(priceableAsset) ? referenceCurve : new SimpleDiscountFactorCurve(baseDate, interp, extrapolationPermitted, points); //The first guess, which should be correct for all priceable assets with analytical solutions that have been implemented. points.Add(assetMaturityDate, (double)priceableAsset.CalculateDiscountFactorAtMaturity(curve)); } IObjectiveFunction objectiveFunction; bool initialValue; if (IsSpreadAsset(priceableAsset)) { objectiveFunction = new RateSpreadAssetQuote(priceableAsset, referenceCurve, baseDate, extrapolationPermitted, points, tolerance); initialValue = ((RateSpreadAssetQuote)objectiveFunction).InitialValue(); } else { objectiveFunction = new RateAssetQuote(priceableAsset, baseDate, interp, extrapolationPermitted, points, tolerance); initialValue = ((RateAssetQuote)objectiveFunction).InitialValue(); } // Check whether the guess was close enough if (!initialValue) { double guess = Math.Max(min, points[assetMaturityDate]); points[assetMaturityDate] = solver.Solve(objectiveFunction, tolerance, guess, min, max); } items.Add(assetMaturityDate, new Pair <string, decimal>(priceableAsset.Id, (decimal)points[assetMaturityDate])); } return(TermPointsFactory.Create(items)); }