/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="baseDate">The base date.</param> /// <param name="extrapolationPermitted">The extrapolationPermitted flag.</param> /// <param name="interpolationMethod">The interpolationMethod.</param> /// <param name="tolerance">Solver tolerance to use.</param> /// <returns></returns> public static TermPoint[] Bootstrap(IEnumerable <IPriceableRateAssetController> priceableAssets, DateTime baseDate, Boolean extrapolationPermitted, InterpolationMethod interpolationMethod, 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; // Add the rest foreach (IPriceableRateAssetController 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); var curve = 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. var curve = 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)); } var objectiveFunction = new RateAssetQuote(priceableAsset, baseDate, interpolationMethod, extrapolationPermitted, points, tolerance); // Check whether the guess was close enough if (!objectiveFunction.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)); }
/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="baseDate">The base date.</param> /// <param name="extrapolationPermitted">The extrapolationPermitted flag.</param> /// <param name="interpolationMethod">The interpolationMethod.</param> /// <param name="tolerance">Solver tolerance to use.</param> /// <returns></returns> public static TermPoint[] Bootstrap(List <IPriceableFxAssetController> priceableAssets, DateTime baseDate, Boolean extrapolationPermitted, InterpolationMethod interpolationMethod, Double tolerance) { const Double solveRateGap = 0.015d;//should we need more precising perhaps??? const Decimal dfamMinThreshold = 0.0m; const Decimal defaultGuess = 0.9m; const Double accuracy = 0.000001d; InterpolationMethod interp = InterpolationMethodHelper.Parse("LinearInterpolation"); //only works for linear on zero. priceableAssets = priceableAssets.OrderBy(a => a.GetRiskMaturityDate()).ToList(); var dates = new List <DateTime>(); var discountFactors = new List <double>(); var items = new Dictionary <DateTime, Pair <string, decimal> >(); bool firstTime = true; foreach (var asset in priceableAssets) { DateTime assetMaturityDate = asset.GetRiskMaturityDate(); //check if the maturity date is already in the list. If not contimue. if (items.ContainsKey(assetMaturityDate)) { continue; } decimal guess = asset.ForwardAtMaturity > dfamMinThreshold ? asset.ForwardAtMaturity : defaultGuess; decimal dfam; if (firstTime) { firstTime = false; dates.Add(assetMaturityDate); discountFactors.Add(Convert.ToDouble(guess)); dfam = asset.CalculateImpliedQuote(new SimpleFxCurve(baseDate, interp, extrapolationPermitted, dates, discountFactors)); discountFactors[0] = (double)dfam; } else { //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. dfam = asset.CalculateImpliedQuote(new SimpleFxCurve(baseDate, interp, extrapolationPermitted, dates, discountFactors)); discountFactors.Add((double)dfam); dates.Add(assetMaturityDate); } //Add a check on the dfam so that the solver is only called if outside the tolerance. var objectiveFunction = new FxAssetQuote(asset, baseDate, interpolationMethod, extrapolationPermitted, dates, discountFactors, tolerance); if (!objectiveFunction.InitialValue()) { var timeInterval = Actual365.Instance.YearFraction(baseDate, assetMaturityDate); var solveInterval = Math.Exp(-solveRateGap * timeInterval); var min = Math.Max(0, (double)dfam * solveInterval); var max = (double)dfam / solveInterval; var solver = new Brent(); dfam = (decimal)solver.Solve(objectiveFunction, accuracy, (double)dfam, min, max); } items.Add(assetMaturityDate, new Pair <string, decimal>(asset.Id, dfam)); } return(TermPointsFactory.Create(items)); }
/// <summary> /// Bootstraps the specified priceable assets, where the assets /// are simple commodity asset with single cash flows on a /// single index observation. /// </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> /// <param name="spreadXArray">THe spread interpolator produced in the bootstrapper</param> /// <param name="spreadYArray">THe spread interpolator produced in the bootstrapper</param> /// <returns></returns> public static TermPoint[] Bootstrap(IEnumerable <IPriceableCommoditySpreadAssetController> priceableAssets, ICommodityCurve referenceCurve, DateTime baseDate, bool extrapolationPermitted, double tolerance, ref IList <double> spreadXArray, ref IList <double> spreadYArray) { //only works for linear on zero. //InterpolationMethod interp = InterpolationMethodHelper.Parse("LinearInterpolation"); // Add the first element (date : discount factor) to the list var points = new Dictionary <DateTime, double>(); var items = new SortedDictionary <DateTime, Pair <string, decimal> >(); var dayCounter = new Actual365(); foreach (var priceableAsset in priceableAssets) { DateTime assetMaturityDate = priceableAsset.GetRiskMaturityDate(); if (points.Keys.Contains(assetMaturityDate)) { continue; } //This now should automatically extrapolate the required discount factor on a flat rate basis. if (IsSpreadAsset(priceableAsset)) { //These are simple assts so the solver is unnecessary. var value = (double)priceableAsset.CalculateImpliedQuoteWithSpread(referenceCurve); var dayCount = dayCounter.YearFraction(baseDate, assetMaturityDate); spreadXArray.Add(dayCount); spreadYArray.Add((double)priceableAsset.MarketQuote.value); //TODO Get the marketquote points.Add(assetMaturityDate, value); items.Add(assetMaturityDate, new Pair <string, decimal>(priceableAsset.Id, (decimal)points[assetMaturityDate])); } } if (spreadXArray.Count > 2) { var spreadCurveInterpolator = new LinearInterpolation(spreadXArray.ToArray(), spreadYArray.ToArray()); var index = 0; foreach (var assetMaturityDate in referenceCurve.GetTermCurve().GetListTermDates()) { if (points.Keys.Contains(assetMaturityDate)) { continue; } var dayCount = dayCounter.YearFraction(baseDate, assetMaturityDate); double spreadValue = spreadCurveInterpolator.ValueAt(dayCount, true); var value = referenceCurve.GetForward(baseDate, assetMaturityDate); points.Add(assetMaturityDate, value + spreadValue); items.Add(assetMaturityDate, new Pair <string, decimal>("RefCurvePillar_" + index, (decimal)points[assetMaturityDate])); index++; } return(TermPointsFactory.Create(items)); } return(null); }
public void LoadInterestRateVolCurveTest() { CreateATMRateVolCurve("AUD", "3M", IrVolInstruments, IrVolValues, IrStrikes); var curve = (CapVolatilityCurve)CurveEngine.LoadVolatilityCurve(Market, "AUD", "3M", null); Assert.IsNotNull(curve); // 3M does exist Assert.AreEqual("3M", curve.GetPricingStructureId().Properties.GetString("IndexTenor", true)); //var baseDate = curve.GetBaseDate(); var expiration = VolatilitySurfaceHelper.GetDimensionValues(curve.GetVolatilityMatrix().dataPoints?.point, CubeDimension.Expiration); var volatilities = VolatilitySurfaceHelper.GetDimensionValues(curve.GetVolatilityMatrix().dataPoints?.point, CubeDimension.Volatility); var valArray = new List <Decimal>(); foreach (var vol in volatilities) { valArray.Add((decimal)vol); } var expiryArray = new List <TimeDimension>(); //var valArray = new List<Decimal>(); //Debug.Print(baseDate.ToString(CultureInfo.InvariantCulture)); foreach (var expiry in expiration) { //Temporary if (expiry is TimeDimension timeDimension) { expiryArray.Add(timeDimension); } //var date = expiry as DateTime? ?? baseDate.AddDays(1); //expiryArray.Add(date); //Debug.Print(expiry.ToString()); } var termPoints = TermPointsFactory.Create(expiryArray, valArray); var termCurve = new TermCurve { point = termPoints }; var output = XmlSerializerHelper.SerializeToString(termCurve); Debug.Print(output); }
/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="baseDate">The base date.</param> /// <param name="extrapolationPermitted">The extrapolationPermitted flag.</param> /// <param name="interpolationMethod">The interpolationMethod.</param> /// <param name="tolerance">Solver tolerance to use.</param> /// <returns></returns> public static TermPoint[] Bootstrap(IEnumerable <IPriceableFuturesAssetController> priceableAssets, DateTime baseDate, Boolean extrapolationPermitted, InterpolationMethod interpolationMethod, Double tolerance) { // Add the first element (date : discount factor) to the list var points = new Dictionary <DateTime, double>(); var items = new Dictionary <DateTime, Pair <string, decimal> >(); // Add the rest foreach (IPriceableFuturesAssetController priceableAsset in priceableAssets) { DateTime assetMaturityDate = priceableAsset.LastTradeDate; if (points.Keys.Contains(assetMaturityDate)) { continue; } points.Add(assetMaturityDate, (double)priceableAsset.IndexAtMaturity); items.Add(assetMaturityDate, new Pair <string, decimal>(priceableAsset.Id, (decimal)points[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="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="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)); }
/// <summary> /// Bootstraps the specified priceable assets. /// </summary> /// <param name="priceableAssets">The priceable assets.</param> /// <param name="baseDate">The base date.</param> /// <param name="extrapolationPermitted">The extrapolationPermitted flag.</param> /// <param name="interpolationMethod">The interpolationMethod.</param> /// <param name="tolerance">Solver tolerance to use.</param> /// <returns></returns> public static TermPoint[] Bootstrap(List <IPriceableCreditAssetController> priceableAssets, DateTime baseDate, Boolean extrapolationPermitted, InterpolationMethod interpolationMethod, Double tolerance) { const Double cSolveRateGap = 0.015d;//should be need more precise perhaps??? const Decimal cDfamMinThreshold = 0.0m; const Decimal cDefaultGuess = 0.9m; const Double accuracy = 0.000001d; priceableAssets.Sort ( (priceableAssetController1, priceableAssetController2) => priceableAssetController1.GetRiskMaturityDate().CompareTo(priceableAssetController2.GetRiskMaturityDate()) ); // Add the first element (date : discount factor) to the list // IList <DateTime> dates = new List <DateTime> { baseDate }; IList <double> discountFactors = new List <double> { 1.0 }; var index = 0; foreach (var priceableAsset in priceableAssets) { //TODO check if the maturity date is already in the list. If not contimue. var assetMaturityDate = priceableAsset.GetRiskMaturityDate(); if (dates.Contains(assetMaturityDate)) { continue; } // do we really need that guess step??? I don't think so... // var guess = priceableAsset.SurvivalProbabilityAtMaturity; if (guess <= cDfamMinThreshold) { guess = cDefaultGuess; } //dates.Add(priceableAsset.GetRiskMaturityDate()); decimal dfam; double[] values; //var position = dates.IndexOf(priceableAsset.GetRiskMaturityDate()); //discountFactors.CopyTo(values, 0); //only works for linear on zero. var interp = InterpolationMethodHelper.Parse("LogLinearInterpolation"); if (index == 0) { dates.Add(priceableAsset.GetRiskMaturityDate()); values = new double[dates.Count]; var position = dates.IndexOf(priceableAsset.GetRiskMaturityDate()); discountFactors.CopyTo(values, 0); values[position] = Convert.ToDouble(guess); dfam = priceableAsset.CalculateImpliedQuote(new SimpleDiscountFactorCurve(baseDate, interp, extrapolationPermitted, dates, values)); values[position] = (double)dfam; index++; } else { //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. var tempvalues = new List <double>(discountFactors); dfam = priceableAsset.CalculateImpliedQuote(new SimpleDiscountFactorCurve(baseDate, interp, extrapolationPermitted, dates, tempvalues.ToArray())); tempvalues.Add((double)dfam); dates.Add(priceableAsset.GetRiskMaturityDate()); values = new double[dates.Count]; tempvalues.CopyTo(values, 0); index++; } //Add a check on the dfam so that the solver is only called if outside tyhe tolerance. var objectiveFunction = new CreditAssetQuote(priceableAsset, baseDate, interpolationMethod, extrapolationPermitted, dates, values, tolerance); if (objectiveFunction.InitialValue()) { discountFactors.Add((double)dfam); } else { var timeInterval = Actual365.Instance.YearFraction(baseDate, assetMaturityDate); var cSolveInterval = Math.Exp(-cSolveRateGap * timeInterval); var min = Math.Max(0, (double)dfam * cSolveInterval); var max = (double)dfam / cSolveInterval; var solver2 = new Brent(); var solvedFiscountFactor = solver2.Solve(objectiveFunction, accuracy, (double)dfam, min, max); //TODO check that this is the correct usage of the optimizer. discountFactors.Add(solvedFiscountFactor); } } return(TermPointsFactory.Create(dates, discountFactors)); }