//internal use + keyPoint manipulation, i.e. solving zspread //Note: this api will bypass curve calibration public YieldCurve( string name, Date referenceDate, Tuple <Date, double>[] keyPoints, BusinessDayConvention bda, IDayCount dayCount, ICalendar calendar, CurrencyCode currency, Compound compound, Interpolation interpolation, YieldCurveTrait trait, IMarketCondition baseMarket = null, Expression <Func <IMarketCondition, object> >[] calibrateMktUpdateCondition = null, ISpread spread = null, string[] keyTenors = null, InstrumentCurveDefinition rawDefinition = null ) { Name = name; ReferenceDate = referenceDate; Currency = currency; Bda = bda; DayCount = dayCount; Compound = compound; Calendar = calendar; Interpolation = interpolation; Trait = trait; BaseMarket = baseMarket; Spread = spread ?? new ZeroSpread(0.0); CalibrateMktUpdateCondition = calibrateMktUpdateCondition; MarketInstruments = null; KeyPoints = keyPoints.OrderBy(x => x.Item1).ToArray(); KeyTenors = keyTenors ?? KeyPoints.Select(x => new Term(x.Item1 - ReferenceDate, Period.Day)).Select(x => x.ToString()).ToArray(); RawDefinition = rawDefinition; InputRateByTenor = KeyPoints.Select(x => Tuple.Create <string, double> (new Term(x.Item1 - ReferenceDate, Period.Day).ToString(), x.Item2)). ToDictionary(v => v.Item1, v => v.Item2); //_curve = new Curve<Date>(ReferenceDate, KeyPoints, x => x.ToOADate(), interpolation); _curveXInYears = new Curve <double>(0.0, KeyPoints.Select(x => Tuple.Create(DayCount.CalcDayCountFraction(ReferenceDate, x.Item1), x.Item2)).ToArray(), x => x, Interpolation); }
public static Tuple <Date, double>[] Calibrate( string name, Date referenceDate, IMarketInstrument[] marketInstruments, BusinessDayConvention bda, IDayCount daycount, ICalendar calendar, Compound compound, Interpolation interpolation, YieldCurveTrait trait, CurrencyCode currency, Date[] knotPoints, out double fittingError, IMarketCondition baseMarket = null, Expression <Func <IMarketCondition, object> >[] expression = null, double initialValue = double.NaN, double initialGuess = 0.05, double xmin = -3, double xmax = 3.0 ) { var accuracy = 1.0e-13; if (marketInstruments.Any(x => !(x.Instrument is Bond))) { throw new PricingLibraryException("All instruments must be bond to build a bond curve!"); } var keyTs = knotPoints.Select(x => daycount.CalcDayCountFraction(referenceDate, x)).ToArray(); var len = keyTs.Length; baseMarket = baseMarket ?? new MarketCondition( x => x.ValuationDate.Value = referenceDate, x => x.DiscountCurve.Value = null, x => x.FixingCurve.Value = null, x => x.HistoricalIndexRates.Value = new Dictionary <IndexType, SortedDictionary <Date, double> >() ); var bondYieldPricer = new BondYieldPricer(); var bonds = marketInstruments.Select(x => x.Instrument as Bond); var bondCf = bonds.Select(x => x.GetCashflows(baseMarket, true)).ToList(); var bondPrices = bonds.Select( (x, i) => bondYieldPricer.FullPriceFromYield(bondCf[i], x.PaymentDayCount, x.PaymentFreq, x.StartDate, referenceDate, marketInstruments[i].TargetValue, x.BondTradeingMarket, x.IrregularPayment)).ToArray(); var rand = new Random(); // variables : alpha, b1, b2, b3, b4, b5 ... var numVars = 1 + 2 + len; var lowerBounds = new double[numVars]; var upperBounds = new double[numVars]; var initials = new double[numVars]; double?finalScore = double.NaN; for (var i = 0; i < numVars; ++i) { lowerBounds[i] = i == 0 ? 0.0 : xmin; upperBounds[i] = i == 0 ? 1.0 : xmax; initials[i] = rand.NextDouble() > 0 ? rand.NextDouble() : -rand.NextDouble(); } var globalSolver = new NLoptSolver(NLoptAlgorithm.GN_DIRECT, (ushort)numVars, accuracy, 100000, NLoptAlgorithm.LN_COBYLA); globalSolver.SetLowerBounds(lowerBounds); globalSolver.SetUpperBounds(upperBounds); globalSolver.SetMinObjective((variables, gradients) => { var error = bondCf.Select((cfs, i) => GetModelPrice(referenceDate, cfs, daycount, variables, keyTs) - bondPrices[i]).ToArray(); return(error.Sum(x => x * x)); }); double?globalFinalScore; var globalResult = globalSolver.Optimize(initials, out globalFinalScore); var localSolvers = new[] { NLoptAlgorithm.LN_BOBYQA, NLoptAlgorithm.LD_AUGLAG, NLoptAlgorithm.LN_COBYLA }; for (var k = 0; k < 3; ++k) { var localSolver = new NLoptSolver(localSolvers[k], (ushort)numVars, accuracy, 100000, NLoptAlgorithm.LN_COBYLA); localSolver.SetLowerBounds(lowerBounds); localSolver.SetUpperBounds(upperBounds); localSolver.SetMinObjective((variables, gradients) => { var error = bondCf.Select((cfs, i) => GetModelPrice(referenceDate, cfs, daycount, variables, keyTs) - bondPrices[i]).ToArray(); return(error.Sum(x => x * x)); }); var result = localSolver.Optimize(initials, out finalScore); } fittingError = finalScore.Value; var coeffes = new[] { Tuple.Create(referenceDate, initials[0]), Tuple.Create(referenceDate, -1.0 - initials[1] - initials[2] - initials.Skip(3).Sum() * 1 / 3.0), Tuple.Create(referenceDate, initials[1]), Tuple.Create(referenceDate, initials[2]), } .Union(knotPoints.Select((x, i) => Tuple.Create(x, initials[i + 3]))) .ToArray(); //var errors = bondCf.Select((cfs, i) => GetModelPrice(referenceDate, cfs, daycount, initials, keyTs)).ToArray(); //for (var i = 0; i < errors.Length; ++i) //{ // Console.WriteLine("{0},{1},{2}", errors[i], bondPrices[i], errors[i] - bondPrices[i]); //} return(coeffes); // Insert 0D point to avoid interpolation jump at the beginning }
//swap curve building for both pricing and pnl, bond curve building only, not for pnl public YieldCurve( string name, Date referenceDate, MarketInstrument[] marketInstruments, BusinessDayConvention bda, IDayCount dayCount, ICalendar calendar, CurrencyCode currency, Compound compound, Interpolation interpolation, YieldCurveTrait trait, IMarketCondition baseMarket = null, Expression <Func <IMarketCondition, object> >[] calibrateMktUpdateCondition = null, ISpread spread = null, Date[] knotPoints = null, string[] keyTenors = null, InstrumentCurveDefinition rawDefinition = null ) { Name = name; ReferenceDate = referenceDate; Currency = currency; Bda = bda; DayCount = dayCount; Compound = compound; Calendar = calendar; Interpolation = interpolation; Trait = trait; RawDefinition = rawDefinition; var tempMarketInstruments = marketInstruments.OrderBy(x => x.Instrument.UnderlyingMaturityDate).ToArray(); var uniqueTenorMktInstruments = new List <MarketInstrument> { tempMarketInstruments[0] }; for (var i = 1; i < tempMarketInstruments.Length; ++i) { if (tempMarketInstruments[i].Instrument.GetCalibrationDate() != tempMarketInstruments[i - 1].Instrument.GetCalibrationDate()) { uniqueTenorMktInstruments.Add(tempMarketInstruments[i]); } } MarketInstruments = uniqueTenorMktInstruments.ToArray(); InputRateByTenor = MarketInstruments.ToDictionary(p => p.Instrument.Tenor, p => p.TargetValue); BaseMarket = baseMarket; Spread = spread ?? new ZeroSpread(0.0); CalibrateMktUpdateCondition = calibrateMktUpdateCondition; if (MarketInstruments.Any(x => x.Instrument is Bond)) { var err = double.NaN; KeyPoints = BondCurveCalibrator.Calibrate(Name, ReferenceDate, MarketInstruments.ToArray(), Bda, DayCount, Calendar, Compound, Interpolation, Trait, Currency, knotPoints, out err, baseMarket, CalibrateMktUpdateCondition); fittingError = err; } else { KeyPoints = YieldCurveCalibrator.Calibrate(name, ReferenceDate, MarketInstruments.ToArray(), Bda, DayCount, Calendar, Compound, Interpolation, Trait, Currency, baseMarket, CalibrateMktUpdateCondition); } //if (KeyPoints.Select(x => x.Item1).Any(z => z is Date)) //{ // _curve = new Curve<Date>(ReferenceDate, KeyPoints, x => x.ToOADate(), interpolation); //} _curveXInYears = new Curve <double>(0.0, KeyPoints.Select(x => Tuple.Create(DayCount.CalcDayCountFraction(ReferenceDate, x.Item1), x.Item2)).ToArray(), x => x, Interpolation); }
public static Tuple <Date, double>[] Calibrate( string name, Date referenceDate, IMarketInstrument[] marketInstruments, BusinessDayConvention bda, IDayCount daycount, ICalendar calendar, Compound compound, Interpolation interpolation, YieldCurveTrait trait, CurrencyCode currency, IMarketCondition baseMarket = null, Expression <Func <IMarketCondition, object> >[] expression = null, double initialValue = double.NaN, double initialGuess = 0.05, //double xmin = -0.9999, double xmin = -0.5, double xmax = 1.0 ) { var accuracy = 1.0e-14; if (marketInstruments.Any(x => x.Instrument is CreditDefaultSwap) || trait == YieldCurveTrait.DiscountCurve) { initialValue = 1.0; initialGuess = 1.0; xmin = 1.0e-64; } var len = marketInstruments.Length; var points = marketInstruments.Select(x => Tuple.Create(x.Instrument.GetCalibrationDate(), initialGuess)).ToList(); var calibrationStartIndex = 0; if (!double.IsNaN(initialValue)) { points.Insert(0, Tuple.Create(referenceDate, initialValue)); calibrationStartIndex = 1; } if (interpolation == Interpolation.ForwardFlat) { //forward flat interpolation, the final instrument is not needed in calibration points = points.Take(len - 1).ToList(); points.Insert(0, Tuple.Create(referenceDate, 0.05)); calibrationStartIndex = 0; } else if (trait == YieldCurveTrait.ForwardCurve) { points.Insert(0, Tuple.Create(referenceDate, 0.05)); calibrationStartIndex = 1; } baseMarket = baseMarket ?? new MarketCondition(x => x.ValuationDate.Value = referenceDate); expression = expression ?? new Expression <Func <IMarketCondition, object> >[] { x => x.DiscountCurve, x => x.FixingCurve }; IYieldCurve calibratedCurve = new YieldCurve(name, referenceDate, points.ToArray(), bda, daycount, calendar, currency, compound, interpolation, trait, baseMarket); var finalInstrument = marketInstruments.Last(); var smooth = (finalInstrument.Instrument is InterestRateSwap); double preCalibrationValue = 0.0; if (smooth) { preCalibrationValue = finalInstrument.Instrument.ModelValue( baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()), finalInstrument.CalibMethod); } while (true) { for (var i = 0; i < len; ++i) { var calibrateFunc = new CalibrateMarketInstrument(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression); //Note: alternative brent solver gives same result, but slower convergence, //var calibrateFunc = new CalibrateMarketInstrument2(marketInstruments[i], calibratedCurve, baseMarket, i + calibrationStartIndex, expression); double finalR; try { finalR = BrentZero.Solve(calibrateFunc, xmin, xmax, accuracy); //finalR = BrentZero2<IUnivariateFunction>.DoSolve(calibrateFunc, xmin, xmax, 0.05, 0); } catch (Exception ex) { throw new PricingLibraryException(string.Format("Error when bootstrapping {0}th point", i)); } calibratedCurve = UpdateKeyRate(calibratedCurve, i + calibrationStartIndex, finalR); } if (!smooth) { break; } else { var postCalibrationValue = finalInstrument.Instrument.ModelValue( baseMarket.UpdateCondition(expression.Select(ex => (IUpdateMktConditionPack) new UpdateMktConditionPack <IYieldCurve>(ex, calibratedCurve)).ToArray()), finalInstrument.CalibMethod); if (Math.Abs(postCalibrationValue - preCalibrationValue) < 1e-12) { break; } preCalibrationValue = postCalibrationValue; } } //if initial value is provided for 0D, 不插0D if (!double.IsNaN(initialValue)) { return(calibratedCurve.KeyPoints.ToArray()); } //NEVER EVER insert points at the end, inset at MOST ONE point at the beginning return(new[] { Tuple.Create(referenceDate, calibratedCurve.KeyPoints[0].Item2) } // Insert 0D point to avoid interpolation jump at the beginning .Union(calibratedCurve.KeyPoints) .ToArray()); }