diff --git a/clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs b/clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs index 99424566..72f445a3 100644 --- a/clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs +++ b/clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs @@ -230,7 +230,7 @@ public static object CreateFundingModel( fxMatrix = fxMatrixCache.GetObject((string)FxMatrix).Value; } - var emptyCurves = new Dictionary(); + var emptyCurves = new Dictionary(); if (fic != null) { emptyCurves = fic.Value.ImplyContainedCurves(BuildDate, Interpolator1DType.LinearFlatExtrap); diff --git a/clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs b/clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs index bcd9318a..2f49d0b7 100644 --- a/clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs +++ b/clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs @@ -32,7 +32,6 @@ public static object CreateCPICurveFromForecasts( [ExcelArgument(Description = "Array of CPI forecasts")] double[] CPIForecasts, [ExcelArgument(Description = "Type of interpolation")] object InterpolationType, [ExcelArgument(Description = "Inflation Index")] string InfIndex, - [ExcelArgument(Description = "Fixing Dictionary")] string FixingDict, [ExcelArgument(Description = "Collateral Spec - default LIBOR.3M")] object CollateralSpec) { return ExcelHelper.Execute(_logger, () => @@ -52,15 +51,8 @@ public static object CreateCPICurveFromForecasts( return $"Rate index {InfIndex} not found in cache"; } - var fixings = new Dictionary(); - if(ContainerStores.GetObjectCache().TryGetObject(FixingDict, out var fixDict)) - { - foreach(var kv in fixDict.Value) - fixings.Add(kv.Key, kv.Value); - } - var pDates = Pillars.ToDateTimeArray(); - var cObj = new CPICurve(BuildDate, pDates, CPIForecasts, rIndex.Value, fixings) + var cObj = new CPICurve(BuildDate, pDates, CPIForecasts, rIndex.Value) { Name = curveName, }; diff --git a/clients/Qwack.Excel.Next/Instruments/FundingInstrumentFunctions.cs b/clients/Qwack.Excel.Next/Instruments/FundingInstrumentFunctions.cs index a62362e4..a50e5b4f 100644 --- a/clients/Qwack.Excel.Next/Instruments/FundingInstrumentFunctions.cs +++ b/clients/Qwack.Excel.Next/Instruments/FundingInstrumentFunctions.cs @@ -533,7 +533,7 @@ public static object CreateFundingInstrumentCollection( var ficInstruments = Instruments.GetAnyFromCache() .SelectMany(s => s); - var fic = new FundingInstrumentCollection(ContainerStores.CurrencyProvider); + var fic = new FundingInstrumentCollection(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider); fic.AddRange(swaps); fic.AddRange(fras); fic.AddRange(futures); diff --git a/clients/Qwack.Excel.Next/Risk/RiskFunctions.cs b/clients/Qwack.Excel.Next/Risk/RiskFunctions.cs index 7a352250..d82057cd 100644 --- a/clients/Qwack.Excel.Next/Risk/RiskFunctions.cs +++ b/clients/Qwack.Excel.Next/Risk/RiskFunctions.cs @@ -240,7 +240,7 @@ public static object PortfolioIrBenchmarkDelta( var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName); var fic = ContainerStores.GetObjectCache().GetObjectOrThrow(FICName, $"FIC {FICName} not found in cache"); var ccy = ContainerStores.CurrencyProvider.GetCurrency(ReportingCcy); - var result = model.BenchmarkRisk(fic.Value, ContainerStores.CurrencyProvider, ccy); + var result = model.BenchmarkRisk(fic.Value, ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider, ccy); return PushCubeToCache(result, ResultObjectName); }); } diff --git a/clients/Qwack.Excel/Curves/IRCurveFunctions.cs b/clients/Qwack.Excel/Curves/IRCurveFunctions.cs index 52d4f9a2..e1611cf8 100644 --- a/clients/Qwack.Excel/Curves/IRCurveFunctions.cs +++ b/clients/Qwack.Excel/Curves/IRCurveFunctions.cs @@ -230,7 +230,7 @@ public static object CreateFundingModel( fxMatrix = fxMatrixCache.GetObject((string)FxMatrix).Value; } - var emptyCurves = new Dictionary(); + var emptyCurves = new Dictionary(); if (fic != null) { emptyCurves = fic.Value.ImplyContainedCurves(BuildDate, Interpolator1DType.LinearFlatExtrap); diff --git a/clients/Qwack.Excel/Instruments/FundingInstrumentFunctions.cs b/clients/Qwack.Excel/Instruments/FundingInstrumentFunctions.cs index 71974ad0..4b6eed77 100644 --- a/clients/Qwack.Excel/Instruments/FundingInstrumentFunctions.cs +++ b/clients/Qwack.Excel/Instruments/FundingInstrumentFunctions.cs @@ -533,7 +533,7 @@ public static object CreateFundingInstrumentCollection( var ficInstruments = Instruments.GetAnyFromCache() .SelectMany(s => s); - var fic = new FundingInstrumentCollection(ContainerStores.CurrencyProvider); + var fic = new FundingInstrumentCollection(ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider); fic.AddRange(swaps); fic.AddRange(fras); fic.AddRange(futures); diff --git a/clients/Qwack.Excel/Risk/RiskFunctions.cs b/clients/Qwack.Excel/Risk/RiskFunctions.cs index bc7ce233..49f0d20e 100644 --- a/clients/Qwack.Excel/Risk/RiskFunctions.cs +++ b/clients/Qwack.Excel/Risk/RiskFunctions.cs @@ -240,7 +240,7 @@ public static object PortfolioIrBenchmarkDelta( var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName); var fic = ContainerStores.GetObjectCache().GetObjectOrThrow(FICName, $"FIC {FICName} not found in cache"); var ccy = ContainerStores.CurrencyProvider.GetCurrency(ReportingCcy); - var result = model.BenchmarkRisk(fic.Value, ContainerStores.CurrencyProvider, ccy); + var result = model.BenchmarkRisk(fic.Value, ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider, ccy); return PushCubeToCache(result, ResultObjectName); }); } diff --git a/examples/Qwack - YeildCurveExample (Frozen Data).xlsx b/examples/Qwack - YeildCurveExample (Frozen Data).xlsx new file mode 100644 index 00000000..6e3ebc98 Binary files /dev/null and b/examples/Qwack - YeildCurveExample (Frozen Data).xlsx differ diff --git a/src/Qwack.Core/Curves/CPICurve.cs b/src/Qwack.Core/Curves/CPICurve.cs index 35b26274..3561b814 100644 --- a/src/Qwack.Core/Curves/CPICurve.cs +++ b/src/Qwack.Core/Curves/CPICurve.cs @@ -14,21 +14,38 @@ public class CPICurve : IIrCurve { public CPICurve() { } - public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, InflationIndex inflationIndex, Dictionary cpiHistory = null) + public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, InflationIndex inflationIndex) { BuildDate = buildDate; Pillars = pillars; CpiRates = cpiRates; InflationIndex = inflationIndex; Basis = InflationIndex.DayCountBasis; - CpiHistory = cpiHistory ?? new Dictionary(); + + BuildInterpolator(); + } + + public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, DayCountBasis cpiBasis, Frequency cpiFixingLag, Calendar cpiCalendar) + { + BuildDate = buildDate; + Pillars = pillars; + CpiRates = cpiRates; + InflationIndex = new InflationIndex + { + DayCountBasis = cpiBasis, + RollConvention = RollType.P, + HolidayCalendars = cpiCalendar, + FixingLag = cpiFixingLag + }; + + Basis = cpiBasis; BuildInterpolator(); } private void BuildInterpolator() { - var allCpiPoints = new Dictionary(CpiHistory.Where(x => x.Key <= BuildDate).ToDictionary(x => x.Key, x => x.Value)); + var allCpiPoints = new Dictionary(); for (var i = 0; i < Pillars.Length; i++) { @@ -49,8 +66,6 @@ private void BuildInterpolator() public DateTime[] Pillars { get; set; } = Array.Empty(); public double[] CpiRates { get; set; } = Array.Empty(); - public Dictionary CpiHistory { get; set; } = new(); - public int NumberOfPillars => Pillars.Length; public InflationIndex InflationIndex { get; set; } @@ -58,6 +73,7 @@ private void BuildInterpolator() private IInterpolator1D _cpiInterp; public int SolveStage { get; set; } + public string CollateralSpec { get; set; } public IIrCurve BumpRate(int pillarIx, double delta, bool mutate) { @@ -69,7 +85,7 @@ public IIrCurve BumpRate(int pillarIx, double delta, bool mutate) } else { - return new CPICurve(BuildDate, Pillars, CpiRates.Select((x, ix) => ix == pillarIx ? x + delta : x).ToArray(), InflationIndex, CpiHistory); + return new CPICurve(BuildDate, Pillars, CpiRates.Select((x, ix) => ix == pillarIx ? x + delta : x).ToArray(), InflationIndex); } } public IIrCurve BumpRateFlat(double delta, bool mutate) @@ -85,18 +101,13 @@ public IIrCurve BumpRateFlat(double delta, bool mutate) } else { - return new CPICurve(BuildDate, Pillars, CpiRates.Select(x => x + delta).ToArray(), InflationIndex, CpiHistory); + return new CPICurve(BuildDate, Pillars, CpiRates.Select(x => x + delta).ToArray(), InflationIndex); } } public Dictionary BumpScenarios(double delta, DateTime lastSensitivityDate) => throw new NotImplementedException(); - public double GetReturn(DateTime startDate, DateTime endDate) - { - var cpiStart = _cpiInterp.Interpolate(startDate.SubtractPeriod(InflationIndex.RollConvention, InflationIndex.HolidayCalendars, InflationIndex.FixingLag).ToOADate()); - var cpiEnd = _cpiInterp.Interpolate(endDate.SubtractPeriod(InflationIndex.RollConvention, InflationIndex.HolidayCalendars, InflationIndex.FixingLag).ToOADate()); - return cpiEnd / cpiStart - 1; - } + public double GetForecast(DateTime fixingDate, int fixingLagInMonths) => InflationUtils.InterpFixing(fixingDate, _cpiInterp, fixingLagInMonths); public double GetDf(DateTime startDate, DateTime endDate) { @@ -130,7 +141,7 @@ public IIrCurve SetRate(int pillarIx, double rate, bool mutate) } else { - return new CPICurve(BuildDate, Pillars, CpiRates.Select((x, ix) => ix == pillarIx ? rate : x).ToArray(), InflationIndex, CpiHistory); + return new CPICurve(BuildDate, Pillars, CpiRates.Select((x, ix) => ix == pillarIx ? rate : x).ToArray(), InflationIndex); } } diff --git a/src/Qwack.Core/Curves/IIrCurve.cs b/src/Qwack.Core/Curves/IIrCurve.cs index 266d0742..cb82bb06 100644 --- a/src/Qwack.Core/Curves/IIrCurve.cs +++ b/src/Qwack.Core/Curves/IIrCurve.cs @@ -26,6 +26,8 @@ public interface IIrCurve IIrCurve RebaseDate(DateTime newAnchorDate); - int SolveStage { get; } + int SolveStage { get; set; } + + string CollateralSpec { get; set; } } } diff --git a/src/Qwack.Core/Curves/IrCurve.cs b/src/Qwack.Core/Curves/IrCurve.cs index 3ba535da..f765b3cd 100644 --- a/src/Qwack.Core/Curves/IrCurve.cs +++ b/src/Qwack.Core/Curves/IrCurve.cs @@ -65,7 +65,7 @@ public IrCurve(TO_IrCurve transportObject, ICurrencyProvider currencyProvider) public Currency Currency; - public string CollateralSpec { get; private set; } + public string CollateralSpec { get; set; } public FloatRateIndex RateIndex { get; private set; } public Dictionary Fixings { get; set; } diff --git a/src/Qwack.Core/Curves/SvenssonCurve.cs b/src/Qwack.Core/Curves/SvenssonCurve.cs index 491e576c..9a10996a 100644 --- a/src/Qwack.Core/Curves/SvenssonCurve.cs +++ b/src/Qwack.Core/Curves/SvenssonCurve.cs @@ -49,14 +49,18 @@ public SvenssonCurve(double beta0, double beta1, double beta2, double beta3, dou public double Beta3 { get; } public double Tau1 { get; } public double Tau2 { get; } - public string CollateralSpec { get; private set; } + public string CollateralSpec { get; set; } public FloatRateIndex RateIndex { get; private set; } public void SetCollateralSpec(string collateralSpec) => CollateralSpec = collateralSpec; public void SetRateIndex(FloatRateIndex rateIndex) => RateIndex = rateIndex; - public int SolveStage { get { throw new NotImplementedException(); } } + public int SolveStage + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } public double GetDf(DateTime startDate, DateTime endDate) { diff --git a/src/Qwack.Core/Instruments/Cashflow.cs b/src/Qwack.Core/Instruments/Cashflow.cs index f5e13794..12a15daf 100644 --- a/src/Qwack.Core/Instruments/Cashflow.cs +++ b/src/Qwack.Core/Instruments/Cashflow.cs @@ -27,6 +27,7 @@ public CashFlow() public double Dcf { get; set; } public Currency Currency { get; set; } public double FixedRateOrMargin { get; set; } + public int CpiFixingLagInMonths { get; set; } public FlowType FlowType { get; set; } public DayCountBasis Basis { get; set; } @@ -51,7 +52,8 @@ public CashFlow() FlowType = FlowType, Basis = Basis, RateIndex = RateIndex, - Dcf = Dcf + Dcf = Dcf, + CpiFixingLagInMonths = CpiFixingLagInMonths }; } diff --git a/src/Qwack.Core/Instruments/CashflowSchedule.cs b/src/Qwack.Core/Instruments/CashflowSchedule.cs index 13ac8aef..054730a3 100644 --- a/src/Qwack.Core/Instruments/CashflowSchedule.cs +++ b/src/Qwack.Core/Instruments/CashflowSchedule.cs @@ -26,7 +26,7 @@ public class CashFlowSchedule public static class CashFlowScheduleEx { - public static double PV(this CashFlowSchedule schedule, IIrCurve discountCurve, IIrCurve forecastCurve, bool updateState, bool updateDf, bool updateEstimate, DayCountBasis basisFloat, DateTime? filterDate) + public static double PV(this CashFlowSchedule schedule, IIrCurve discountCurve, IIrCurve forecastCurve, bool updateState, bool updateDf, bool updateEstimate, DayCountBasis basisFloat, DateTime? filterDate, double? initialCpiFixing = null) { double totalPv = 0; @@ -135,8 +135,8 @@ public static double PV(this CashFlowSchedule schedule, IIrCurve discountCurve, } var s = flow.AccrualPeriodStart; var e = flow.AccrualPeriodEnd; - var cpiStart = forecastCurve.GetRate(s); - var cpiEnd = forecastCurve.GetRate(e); + var cpiStart = initialCpiFixing ?? forecastCurve.GetRate(s); + var cpiEnd = infCurve.GetForecast(e, flow.CpiFixingLagInMonths); fv = cpiEnd / cpiStart * flow.Notional * flow.FixedRateOrMargin * flow.YearFraction; } else diff --git a/src/Qwack.Core/Instruments/Funding/FundingInstrumentCollection.cs b/src/Qwack.Core/Instruments/Funding/FundingInstrumentCollection.cs index 2865cbcb..fcb36293 100644 --- a/src/Qwack.Core/Instruments/Funding/FundingInstrumentCollection.cs +++ b/src/Qwack.Core/Instruments/Funding/FundingInstrumentCollection.cs @@ -4,6 +4,7 @@ using Qwack.Core.Basic; using Qwack.Core.Curves; using Qwack.Core.Models; +using Qwack.Dates; using Qwack.Math.Interpolation; using Qwack.Transport.BasicTypes; @@ -16,39 +17,57 @@ namespace Qwack.Core.Instruments.Funding public class FundingInstrumentCollection : List { private readonly ICurrencyProvider _currencyProvider; + private readonly ICalendarProvider _calendarProvider; public List SolveCurves => this.Select(x => x.SolveCurve).Distinct().ToList(); - public FundingInstrumentCollection(ICurrencyProvider currencyProvider) => _currencyProvider = currencyProvider; + public FundingInstrumentCollection(ICurrencyProvider currencyProvider, ICalendarProvider calendarProvider) + { + _currencyProvider = currencyProvider; + _calendarProvider = calendarProvider; + } public FundingInstrumentCollection Clone() { - var fic = new FundingInstrumentCollection(_currencyProvider); + var fic = new FundingInstrumentCollection(_currencyProvider, _calendarProvider); fic.AddRange(this.Select(x => x.Clone())); return fic; } - public Dictionary ImplyContainedCurves(DateTime buildDate, Interpolator1DType interpType) + public Dictionary ImplyContainedCurves(DateTime buildDate, Interpolator1DType interpType) { - var o = new Dictionary(); + var o = new Dictionary(); foreach (var curveName in SolveCurves) { - var pillars = this.Where(x => x.SolveCurve == curveName) - .Select(x => x.PillarDate) + var insInScope = this.Where(x => x.SolveCurve == curveName).ToList(); + var pillars = insInScope.Select(x => x.PillarDate) .OrderBy(x => x) .ToArray(); - if (pillars.Distinct().Count() != pillars.Count()) + if (pillars.Distinct().Count() != pillars.Length) throw new Exception($"More than one instrument has the same solve pillar on curve {curveName}"); - var dummyRates = pillars.Select(x => 0.05).ToArray(); var ccy = _currencyProvider.GetCurrency(curveName.Split('.')[0]); var colSpec = (curveName.Contains("[")) ? curveName.Split('[').Last().Trim("[]".ToCharArray()) : curveName.Substring(curveName.IndexOf('.') + 1); if (o.Values.Any(v => v.CollateralSpec == colSpec)) colSpec = colSpec + "_" + curveName; - var irCurve = new IrCurve(pillars, dummyRates, buildDate, curveName, interpType, ccy, colSpec); - o.Add(curveName, irCurve); + if (insInScope.All(i => i is IIsInflationInstrument)) + { + var dummyRates = pillars.Select(x => 100.0).ToArray(); + var irCurve = new CPICurve(buildDate, pillars, dummyRates, DayCountBasis.Act360, new Frequency("-3m"), _calendarProvider.GetCalendarSafe(ccy)) + { + Name = curveName, + CollateralSpec = colSpec, + }; + o.Add(curveName, irCurve); + } + else + { + var dummyRates = pillars.Select(x => 0.05).ToArray(); + var irCurve = new IrCurve(pillars, dummyRates, buildDate, curveName, interpType, ccy, colSpec); + o.Add(curveName, irCurve); + } } return o; } diff --git a/src/Qwack.Core/Instruments/Funding/InflationPerformanceSwap.cs b/src/Qwack.Core/Instruments/Funding/InflationPerformanceSwap.cs index 56434e73..337db28a 100644 --- a/src/Qwack.Core/Instruments/Funding/InflationPerformanceSwap.cs +++ b/src/Qwack.Core/Instruments/Funding/InflationPerformanceSwap.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.WebSockets; using Qwack.Core.Basic; using Qwack.Core.Curves; using Qwack.Core.Models; @@ -9,7 +10,7 @@ namespace Qwack.Core.Instruments.Funding { - public class InflationPerformanceSwap : IFundingInstrument, ISaCcrEnabledIR + public class InflationPerformanceSwap : IFundingInstrument, ISaCcrEnabledIR, IIsInflationInstrument { public Dictionary MetaData { get; set; } = new Dictionary(); public InflationPerformanceSwap() { } @@ -44,7 +45,7 @@ public InflationPerformanceSwap(DateTime startDate, Frequency swapTenor, Inflati public double ParRate { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } - public int NDates { get; set; } + public double InitialFixing { get; set; } public DateTime[] ResetDates { get; set; } public Currency Currency { get; set; } @@ -70,8 +71,18 @@ public double Pv(IFundingModel model, bool updateState) { var discountCurve = model.Curves[DiscountCurve]; var forecastCurveCpi = model.Curves[ForecastCurveCpi]; - - var cpiPerf = (forecastCurveCpi as CPICurve).GetReturn(StartDate, EndDate); + + if (InitialFixing == 0) + { + if (!model.TryGetFixingDictionary(ForecastCurveCpi, out var fixingDictionary)) + throw new Exception($"Fixing dictionary not found for inflation index {ForecastCurveCpi}"); + + InitialFixing = InflationUtils.InterpFixing(StartDate, fixingDictionary, RateIndex.FixingLag.PeriodCount); + } + + var forecast = (forecastCurveCpi as CPICurve).GetForecast(EndDate, RateIndex.FixingLag.PeriodCount); + + var cpiPerf = forecast / InitialFixing - 1; var cpiLegFv = cpiPerf * Notional * (SwapType == SwapPayReceiveType.Payer ? 1.0 : -1.0); var fixedLegFv = FixedFlow; @@ -89,10 +100,19 @@ public Dictionary> Sensitivities(IFundingMo public double CalculateParRate(IFundingModel model) { var forecastCurveCpi = model.Curves[ForecastCurveCpi]; + if (InitialFixing == 0) + { + if (!model.TryGetFixingDictionary(ForecastCurveCpi, out var fixingDictionary)) + throw new Exception($"Fixing dictionary not found for inflation index {ForecastCurveCpi}"); + + InitialFixing = InflationUtils.InterpFixing(StartDate, fixingDictionary, RateIndex.FixingLag.PeriodCount); + } - var cpiPerf = (forecastCurveCpi as CPICurve).GetReturn(StartDate, EndDate); + var forecast = (forecastCurveCpi as CPICurve).GetForecast(EndDate, RateIndex.FixingLag.PeriodCount); - return System.Math.Pow(cpiPerf + 1, 1 / T) - 1; + var cpiPerf = forecast / InitialFixing; + + return System.Math.Pow(cpiPerf, 1 / T) - 1; } public IFundingInstrument Clone() => new InflationPerformanceSwap @@ -105,7 +125,7 @@ public double CalculateParRate(IFundingModel model) EndDate = EndDate, FixedFlow = FixedFlow, ForecastCurveCpi = ForecastCurveCpi, - NDates = NDates, + InitialFixing = InitialFixing, Notional = Notional, ParRate = ParRate, PillarDate = PillarDate, @@ -147,7 +167,17 @@ public List ExpectedCashFlows(IAssetFxModel model) var discountCurve = model.FundingModel.Curves[DiscountCurve]; var forecastCurveCpi = model.FundingModel.Curves[ForecastCurveCpi]; - var cpiPerf = (forecastCurveCpi as CPICurve).GetReturn(StartDate, EndDate); + if (InitialFixing == 0) + { + if (!model.TryGetFixingDictionary(ForecastCurveCpi, out var fixingDictionary)) + throw new Exception($"Fixing dictionary not found for inflation index {ForecastCurveCpi}"); + + InitialFixing = InflationUtils.InterpFixing(StartDate, fixingDictionary, RateIndex.FixingLag.PeriodCount); + } + + var forecast = (forecastCurveCpi as CPICurve).GetForecast(EndDate, RateIndex.FixingLag.PeriodCount); + + var cpiPerf = forecast / InitialFixing; var cpiLegFv = cpiPerf * Notional * (SwapType == SwapPayReceiveType.Payer ? 1.0 : -1.0); var fixedLegFv = FixedFlow; diff --git a/src/Qwack.Core/Instruments/Funding/InflationSwap.cs b/src/Qwack.Core/Instruments/Funding/InflationSwap.cs index 193e5405..10dfb07d 100644 --- a/src/Qwack.Core/Instruments/Funding/InflationSwap.cs +++ b/src/Qwack.Core/Instruments/Funding/InflationSwap.cs @@ -2,13 +2,14 @@ using System.Collections.Generic; using System.Linq; using Qwack.Core.Basic; +using Qwack.Core.Curves; using Qwack.Core.Models; using Qwack.Dates; using Qwack.Transport.BasicTypes; namespace Qwack.Core.Instruments.Funding { - public class InflationSwap : IFundingInstrument, ISaCcrEnabledIR + public class InflationSwap : IFundingInstrument, ISaCcrEnabledIR, IIsInflationInstrument { public Dictionary MetaData { get; set; } = new Dictionary(); public InflationSwap() { } @@ -44,6 +45,10 @@ public InflationSwap(DateTime startDate, Frequency swapTenor, InflationIndex rat AccrualDCB = rateIndex.DayCountBasis }; FlowScheduleCpiLinked = CpiLeg.GenerateSchedule(); + foreach(var flow in FlowScheduleCpiLinked.Flows) + { + flow.CpiFixingLagInMonths = rateIndex.FixingLag.PeriodCount; + } FlowScheduleIrLinked = IrLeg.GenerateSchedule(); ResetDates = FlowScheduleIrLinked.Flows.Select(x => x.FixingDateStart).ToArray(); @@ -57,7 +62,7 @@ public InflationSwap(DateTime startDate, Frequency swapTenor, InflationIndex rat public double ParRate { get; set; } public DateTime StartDate { get; set; } public DateTime EndDate { get; set; } - public int NDates { get; set; } + public double InitialFixing { get; set; } public DateTime[] ResetDates { get; set; } public Currency Currency { get; set; } public GenericSwapLeg CpiLeg { get; set; } @@ -78,6 +83,7 @@ public InflationSwap(DateTime startDate, Frequency swapTenor, InflationIndex rat public string Counterparty { get; set; } public InflationIndex RateIndex { get; set; } public string PortfolioName { get; set; } + public DateTime LastSensitivityDate => EndDate; @@ -91,7 +97,15 @@ public double Pv(IFundingModel model, bool updateState) var discountCurve = model.Curves[DiscountCurve]; var forecastCurveCpi = model.Curves[ForecastCurveCpi]; var forecastCurveIr = model.Curves[ForecastCurveIr]; - var cpiLegPv = FlowScheduleCpiLinked.PV(discountCurve, forecastCurveCpi, updateState, updateDf, updateEst, BasisFloat, null); + + if (InitialFixing == 0) + { + if (!model.TryGetFixingDictionary(ForecastCurveCpi, out var fixingDictionary)) + throw new Exception($"Fixing dictionary not found for inflation index {ForecastCurveCpi}"); + + InitialFixing = InflationUtils.InterpFixing(StartDate, fixingDictionary, RateIndex.FixingLag.PeriodCount); + } + var cpiLegPv = FlowScheduleCpiLinked.PV(discountCurve, forecastCurveCpi, updateState, updateDf, updateEst, BasisFloat, null, InitialFixing); var irLegPv = FlowScheduleIrLinked.PV(discountCurve, forecastCurveIr, updateState, updateDf, updateEst, BasisFloat, null); return cpiLegPv + irLegPv; @@ -173,7 +187,7 @@ public double CalculateParRate(IFundingModel model) FlowScheduleCpiLinked = FlowScheduleCpiLinked.Clone(), FlowScheduleIrLinked = FlowScheduleIrLinked.Clone(), ForecastCurveCpi = ForecastCurveCpi, - NDates = NDates, + InitialFixing = InitialFixing, Notional = Notional, ParRate = ParRate, PillarDate = PillarDate, diff --git a/src/Qwack.Core/Instruments/Funding/InflationUtils.cs b/src/Qwack.Core/Instruments/Funding/InflationUtils.cs new file mode 100644 index 00000000..c1d63e9f --- /dev/null +++ b/src/Qwack.Core/Instruments/Funding/InflationUtils.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Qwack.Core.Models; +using Qwack.Math; + +namespace Qwack.Core.Instruments.Funding +{ + public static class InflationUtils + { + public static double InterpFixing(DateTime fixingDate, double indexA, double indexB) + { + var d = fixingDate.Day; + var som = new DateTime(fixingDate.Year, fixingDate.Month, 1); + var D = som.AddMonths(1).Subtract(som).TotalDays; + + return indexA + d / D * (indexB - indexA); + } + + public static double InterpFixing(DateTime fixingDate, IInterpolator1D fixings, int fixingLagMonths) + { + var d0 = fixingDate.AddMonths(-System.Math.Abs(fixingLagMonths)); + var indexA = fixings.Interpolate(new DateTime(d0.Year, d0.Month, 1).ToOADate()); + var indexB = fixings.Interpolate(new DateTime(d0.Year, d0.Month, 1).AddMonths(1).ToOADate()); + return InterpFixing(fixingDate, indexA, indexB); + } + + public static double InterpFixing(DateTime fixingDate, IFixingDictionary fixings, int fixingLagMonths) + { + var d0 = fixingDate.AddMonths(-System.Math.Abs(fixingLagMonths)); + var som = new DateTime(d0.Year, d0.Month, 1); + var indexA = fixings[som]; + if (som == d0) + return indexA; + var indexB = fixings[som.AddMonths(1)]; + return InterpFixing(fixingDate, indexA, indexB); + } + } +} diff --git a/src/Qwack.Core/Instruments/IIsInflationInstrument.cs b/src/Qwack.Core/Instruments/IIsInflationInstrument.cs new file mode 100644 index 00000000..d5d1bbfa --- /dev/null +++ b/src/Qwack.Core/Instruments/IIsInflationInstrument.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Qwack.Core.Instruments +{ + public interface IIsInflationInstrument + { + } +} diff --git a/src/Qwack.Core/Models/IFundingModel.cs b/src/Qwack.Core/Models/IFundingModel.cs index 64616e60..9e54157d 100644 --- a/src/Qwack.Core/Models/IFundingModel.cs +++ b/src/Qwack.Core/Models/IFundingModel.cs @@ -39,5 +39,10 @@ public interface IFundingModel Dictionary CalibrationCurves { get; set; } TO_FundingModel GetTransportObject(); + + IFixingDictionary GetFixingDictionary(string name); + bool TryGetFixingDictionary(string name, out IFixingDictionary fixings); + void AddFixingDictionary(string name, IFixingDictionary fixings); + void RemoveFixingDictionary(string name); } } diff --git a/src/Qwack.Models/Calibrators/CMEModelBuilder.cs b/src/Qwack.Models/Calibrators/CMEModelBuilder.cs index d4e40142..42096927 100644 --- a/src/Qwack.Models/Calibrators/CMEModelBuilder.cs +++ b/src/Qwack.Models/Calibrators/CMEModelBuilder.cs @@ -37,7 +37,7 @@ public static IrCurve GetCurveForCode(IEnumerable parsed, string var origin = DateTime.ParseExact(parsed.First().BizDt, "yyyy-MM-dd", CultureInfo.InvariantCulture); var instruments = parsed.Select(p => ToQwackIns(p, qwackCode, futureSettingsProvider, currencyProvider, indices, curves)).ToList(); var pillars = instruments.Select(x => x.PillarDate).OrderBy(x => x).ToArray(); - var fic = new FundingInstrumentCollection(currencyProvider); + var fic = new FundingInstrumentCollection(currencyProvider, calendarProvider); fic.AddRange(instruments); var curve = new IrCurve(pillars, pillars.Select(p => 0.01).ToArray(), origin, curveName, Interpolator1DType.Linear, currencyProvider.GetCurrency("USD")) { @@ -144,7 +144,7 @@ public static IrCurve StripFxBasisCurve(string cmeFwdFileName, FxPair ccyPair, s ForeignDiscountCurve = ccyPair.Foreign == curveCcy ? curveName : baseCurve.Name, }); - var fic = new FundingInstrumentCollection(currencyProvider); + var fic = new FundingInstrumentCollection(currencyProvider, calendarProvider); fic.AddRange(fwdObjects); var pillars = fwds.Keys.OrderBy(x => x).ToArray(); var curve = new IrCurve(pillars, pillars.Select(p => 0.01).ToArray(), valDate, curveName, Interpolator1DType.Linear, curveCcy); @@ -190,7 +190,7 @@ public static IrCurve StripFxBasisCurve(Dictionary fwds, FxPai ForeignDiscountCurve = ccyPair.Foreign == curveCcy ? curveName : baseCurve.Name, }); - var fic = new FundingInstrumentCollection(currencyProvider); + var fic = new FundingInstrumentCollection(currencyProvider, calendarProvider); fic.AddRange(fwdObjects); var pillars = fwds.Keys.OrderBy(x => x).ToArray(); var curve = new IrCurve(pillars, pillars.Select(p => 0.01).ToArray(), valDate, curveName, Interpolator1DType.Linear, curveCcy); diff --git a/src/Qwack.Models/Calibrators/COMEXModelBuilder.cs b/src/Qwack.Models/Calibrators/COMEXModelBuilder.cs index d138358d..36f72a38 100644 --- a/src/Qwack.Models/Calibrators/COMEXModelBuilder.cs +++ b/src/Qwack.Models/Calibrators/COMEXModelBuilder.cs @@ -40,7 +40,7 @@ public static (IrCurve curve, double spotPrice) GetMetalCurveForCode(string cmxS ForeignDiscountCurve = baseCurve.Name, }); - var fic = new FundingInstrumentCollection(currencyProvider); + var fic = new FundingInstrumentCollection(currencyProvider, calendarProvider); fic.AddRange(fwdObjects); var pillars = fwds.Keys.OrderBy(x => x).ToArray(); var curve = new IrCurve(pillars, pillars.Select(p => 0.01).ToArray(), valDate, curveName, Interpolator1DType.Linear, curveCcy); diff --git a/src/Qwack.Models/Calibrators/QuickFundingCurveStripper.cs b/src/Qwack.Models/Calibrators/QuickFundingCurveStripper.cs index df5a41af..887adea2 100644 --- a/src/Qwack.Models/Calibrators/QuickFundingCurveStripper.cs +++ b/src/Qwack.Models/Calibrators/QuickFundingCurveStripper.cs @@ -19,7 +19,7 @@ public static IrCurve StripFlatSpread(string name, double spread, FloatRateIndex var instruments = pillars.Select(p => new FloatingRateLoanDepo(baseCurve.BuildDate, p, floatRateIndex, 1e6, spread, baseCurve.Name, name) //-spread hack { SolveCurve = name, PillarDate = p }).ToArray(); - var fic = new FundingInstrumentCollection(currencyProvider); + var fic = new FundingInstrumentCollection(currencyProvider, calendarProvider); fic.AddRange(instruments); var solver = new NewtonRaphsonMultiCurveSolverStaged(); var fm = new FundingModel(baseCurve.BuildDate, new[] { baseCurve, fCurve }, currencyProvider, calendarProvider); diff --git a/src/Qwack.Models/Models/FundingModel.cs b/src/Qwack.Models/Models/FundingModel.cs index 5f21372f..f3f95e46 100644 --- a/src/Qwack.Models/Models/FundingModel.cs +++ b/src/Qwack.Models/Models/FundingModel.cs @@ -97,6 +97,23 @@ public IIrCurve GetCurveByCCyAndSpec(Currency ccy, string collateralSpec) public Dictionary CalibrationCurves { get; set; } public void UpdateCurves(Dictionary updateCurves) => Curves = new Dictionary(updateCurves); + private readonly Dictionary _fixings = new(); + + public void AddFixingDictionary(string name, IFixingDictionary fixings) => _fixings[name] = fixings; + public void AddFixingDictionaries(Dictionary fixings) + { + foreach (var kv in fixings) + _fixings[kv.Key] = kv.Value; + } + public IFixingDictionary GetFixingDictionary(string name) + { + if (!_fixings.TryGetValue(name, out var dict)) + throw new Exception($"Fixing dictionary with name {name} not found"); + return dict; + } + public bool TryGetFixingDictionary(string name, out IFixingDictionary fixings) => _fixings.TryGetValue(name, out fixings); + public void RemoveFixingDictionary(string name) => _fixings.Remove(name); + public IFundingModel BumpCurve(string curveName, int pillarIx, double deltaBump, bool mutate) { var newModel = new FundingModel(BuildDate, Curves.Select(kv => diff --git a/src/Qwack.Models/Risk/BenchmarkCurveRisk.cs b/src/Qwack.Models/Risk/BenchmarkCurveRisk.cs index 8d624ff8..42c27866 100644 --- a/src/Qwack.Models/Risk/BenchmarkCurveRisk.cs +++ b/src/Qwack.Models/Risk/BenchmarkCurveRisk.cs @@ -17,7 +17,7 @@ namespace Qwack.Models.Risk { public static class BenchmarkCurveRisk { - public static ICube BenchmarkRiskWithReStrip(this IPvModel pvModel, FundingInstrumentCollection riskCollection, ICurrencyProvider currencyProvider, Currency reportingCcy) + public static ICube BenchmarkRiskWithReStrip(this IPvModel pvModel, FundingInstrumentCollection riskCollection, ICurrencyProvider currencyProvider, ICalendarProvider calendarProvider, Currency reportingCcy) { var insByCurve = riskCollection.GroupBy(x => x.SolveCurve); var curvesNeeded = insByCurve.Select(x => x.Key).ToArray(); @@ -42,10 +42,10 @@ public static ICube BenchmarkRiskWithReStrip(this IPvModel pvModel, FundingInstr sol.Solve(newFundingModel, riskCollection); var newModel = pvModel.VanillaModel.Clone(newFundingModel); - return newModel.BenchmarkRisk(riskCollection, currencyProvider, reportingCcy); + return newModel.BenchmarkRisk(riskCollection, currencyProvider, calendarProvider, reportingCcy); } - public static ICube BenchmarkRisk(this IPvModel pvModel, FundingInstrumentCollection riskCollection, ICurrencyProvider currencyProvider, Currency reportingCcy) + public static ICube BenchmarkRisk(this IPvModel pvModel, FundingInstrumentCollection riskCollection, ICurrencyProvider currencyProvider, ICalendarProvider calendarProvider, Currency reportingCcy) { var cube = new ResultCube(); var dataTypes = new Dictionary @@ -121,7 +121,7 @@ public static ICube BenchmarkRisk(this IPvModel pvModel, FundingInstrumentCollec var parRates = insToRisk.Select(x => x.CalculateParRate(pvModel.VanillaModel.FundingModel)).ToList(); var newIns = insToRisk.Select((x, ix) => x.SetParRate(parRates[ix])); - var newFic = new FundingInstrumentCollection(currencyProvider); + var newFic = new FundingInstrumentCollection(currencyProvider, calendarProvider); newFic.AddRange(newIns.OrderBy(x => x.SolveCurve).ThenBy(x => x.PillarDate)); var fModel = pvModel.VanillaModel.FundingModel.DeepClone(null); @@ -150,7 +150,7 @@ public static ICube BenchmarkRisk(this IPvModel pvModel, FundingInstrumentCollec var bumpSize = GetBumpSize(insToRisk[i]); var bumpedIns = newIns.Select((x, ix) => x.SetParRate(parRates[ix] + (ix == i ? bumpSize : 0.0))); - var newFicb = new FundingInstrumentCollection(currencyProvider); + var newFicb = new FundingInstrumentCollection(currencyProvider, calendarProvider); newFicb.AddRange(bumpedIns); var fModelb = fModel.DeepClone(null); diff --git a/test/Qwack.Core.Tests/CurveSolving/EuroDollarFuturesFact.cs b/test/Qwack.Core.Tests/CurveSolving/EuroDollarFuturesFact.cs index 05cc4dd5..174c7ea5 100644 --- a/test/Qwack.Core.Tests/CurveSolving/EuroDollarFuturesFact.cs +++ b/test/Qwack.Core.Tests/CurveSolving/EuroDollarFuturesFact.cs @@ -67,7 +67,7 @@ public void FuturesStripNoConvexity() currentDate = currentDate.AddMonths(3); } - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); fic.AddRange(instruments); var curve = new IrCurve(pillars, new double[nContracts], startDate, "USD.LIBOR.3M", Interpolator1DType.LinearFlatExtrap, ccyUsd); @@ -138,7 +138,7 @@ public void FuturesStripWithConvexity() currentDate = currentDate.AddMonths(3); } - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); fic.AddRange(instruments); var curve = new IrCurve(pillars, new double[nContracts], startDate, "USD.LIBOR.3M", Interpolator1DType.LinearFlatExtrap, ccyUsd); diff --git a/test/Qwack.Core.Tests/CurveSolving/OisFact.cs b/test/Qwack.Core.Tests/CurveSolving/OisFact.cs index 791cbbfc..532918f4 100644 --- a/test/Qwack.Core.Tests/CurveSolving/OisFact.cs +++ b/test/Qwack.Core.Tests/CurveSolving/OisFact.cs @@ -93,7 +93,7 @@ public void BasicOisCurveSolving() var oisSwaps = new IrBasisSwap[oisTenors.Length]; var FRAs = new ForwardRateAgreement[FRATenors.Length]; - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); for (var i = 0; i < FRAs.Length; i++) { @@ -193,7 +193,7 @@ public void ComplexCurve() var USDFRAs = new ForwardRateAgreement[FRATenors.Length]; - var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); for (var i = 0; i < FRATenors.Length; i++) { @@ -352,7 +352,7 @@ public void ComplexerCurve() var fxForwards = new FxForward[fxForwardTenors.Length]; var xcySwaps = new XccyBasisSwap[xcySwapTenors.Length]; - var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); for (var i = 0; i < FRATenors.Length; i++) { @@ -481,7 +481,7 @@ public void LessComplexCurve() var ZARdepos = new IrSwap[depoTenors.Length]; var ZARFRAs = new ForwardRateAgreement[FRATenors.Length]; - var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + var FIC = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); for (var i = 0; i < FRATenors.Length; i++) { diff --git a/test/Qwack.Core.Tests/CurveSolving/SelfDiscountingFact.cs b/test/Qwack.Core.Tests/CurveSolving/SelfDiscountingFact.cs index 56408b50..5489b472 100644 --- a/test/Qwack.Core.Tests/CurveSolving/SelfDiscountingFact.cs +++ b/test/Qwack.Core.Tests/CurveSolving/SelfDiscountingFact.cs @@ -46,7 +46,7 @@ public void BasicSelfDiscounting() var swap2 = new IrSwap(startDate, swapTenor2, zar3m, 0.06, SwapPayReceiveType.Payer, "ZAR.JIBAR.3M", "ZAR.JIBAR.3M"); var depo = new IrSwap(startDate, 3.Months(), zar3m, 0.06, SwapPayReceiveType.Payer, "ZAR.JIBAR.3M", "ZAR.JIBAR.3M"); - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { swap, swap2, diff --git a/test/Qwack.Core.Tests/Inflation/InflationCurveFacts.cs b/test/Qwack.Core.Tests/Inflation/InflationCurveFacts.cs index 1749e377..a4820c11 100644 --- a/test/Qwack.Core.Tests/Inflation/InflationCurveFacts.cs +++ b/test/Qwack.Core.Tests/Inflation/InflationCurveFacts.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; +using Qwack.Core.Basic; using Qwack.Core.Curves; using Qwack.Core.Instruments; using Qwack.Core.Instruments.Funding; @@ -20,7 +21,7 @@ public void TestInflationCurve() { var vd = new DateTime(2023, 03, 01); var usd = TestProviderHelper.CurrencyProvider.GetCurrencySafe("USD"); - var usdIrCurve = new ConstantRateIrCurve(0.05, vd, "USD-CURVE", usd) + var usdIrCurve = new ConstantRateIrCurve(0.05, vd, "USD.CURVE", usd) { SolveStage = -1, }; @@ -35,16 +36,16 @@ public void TestInflationCurve() ResetFrequency = 1.Years() }; - var infSwap1y = new InflationSwap(vd, 1.Years(), infIx, 0.045, Core.Basic.SwapPayReceiveType.Pay, "USD-CPI", "USD-CURVE", "USD-CURVE") + var infSwap1y = new InflationSwap(vd, 1.Years(), infIx, 0.045, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE", "USD.CURVE") { - SolveCurve = "USD-CPI" + SolveCurve = "USD.CPI" }; - var infSwap2y = new InflationSwap(vd, 2.Years(), infIx, 0.045, Core.Basic.SwapPayReceiveType.Pay, "USD-CPI", "USD-CURVE", "USD-CURVE") + var infSwap2y = new InflationSwap(vd, 2.Years(), infIx, 0.045, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE", "USD.CURVE") { - SolveCurve = "USD-CPI" + SolveCurve = "USD.CPI" }; - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { infSwap1y, infSwap2y @@ -60,13 +61,14 @@ public void TestInflationCurve() var pillars = new[] { vd.AddYears(1), vd.AddYears(2) }; var rates = new[] { 120.0, 121.0 }; - var cpiCurve = new CPICurve(vd, pillars, rates, infIx, fixings) + var cpiCurve = new CPICurve(vd, pillars, rates, infIx) { - Name = "USD-CPI", + Name = "USD.CPI", SolveStage = 0, }; var model = new FundingModel(vd, new IIrCurve[] { usdIrCurve, cpiCurve }, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); + model.AddFixingDictionary("USD.CPI", new FixingDictionary(fixings)); var S = new NewtonRaphsonMultiCurveSolverStaged { @@ -85,7 +87,7 @@ public void TestInflationPerfSwap() { var vd = new DateTime(2023, 02, 01); var usd = TestProviderHelper.CurrencyProvider.GetCurrencySafe("USD"); - var usdIrCurve = new ConstantRateIrCurve(0.05, vd, "USD-CURVE", usd) + var usdIrCurve = new ConstantRateIrCurve(0.05, vd, "USD.CURVE", usd) { SolveStage = -1, }; @@ -102,19 +104,33 @@ public void TestInflationPerfSwap() var pillars = new DateTime[] { vd, vd.AddYears(1).AddMonths(-1) }; var cpiRates = new double[] { 100, 150 }; - var cpiCurve = new CPICurve(vd, pillars, cpiRates, infIx, new Dictionary { { vd.AddMonths(-1), 100.0 } }) { Name = "USD-CPI" }; + var cpiCurve = new CPICurve(vd, pillars, cpiRates, infIx) { Name = "USD.CPI" }; + var fixingDict = new Dictionary { { vd.AddMonths(-1), 100.0 } }; var fModel = new FundingModel(vd, new IIrCurve[] { usdIrCurve, cpiCurve }, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); - - var infSwap1y = new InflationPerformanceSwap(vd, 1.Years(), infIx, 0.045, 1e6, Core.Basic.SwapPayReceiveType.Pay, "USD-CPI", "USD-CURVE") + fModel.AddFixingDictionary("USD.CPI", new FixingDictionary(fixingDict)); + var infSwap1y = new InflationPerformanceSwap(vd, 1.Years(), infIx, 0.045, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE") { - SolveCurve = "USD-CPI", + SolveCurve = "USD.CPI", }; - Assert.Equal(0.5, infSwap1y.CalculateParRate(fModel), 3); + Assert.Equal(0.5, infSwap1y.CalculateParRate(fModel), 2); Assert.NotEqual(0, infSwap1y.Pv(fModel, false), 3); infSwap1y = infSwap1y.SetParRate(infSwap1y.CalculateParRate(fModel)) as InflationPerformanceSwap; Assert.Equal(0, infSwap1y.Pv(fModel, false), 8); } + + + [Fact] + public void TestInfInterpolation() + { + var indexA = 100; + var indexB = 110; + var fDate = new DateTime(2023, 6, 15); + + var sut = InflationUtils.InterpFixing(fDate, indexA, indexB); + + Assert.Equal(105, sut, 8); + } } } diff --git a/test/Qwack.Core.Tests/Instruments/FICFacts.cs b/test/Qwack.Core.Tests/Instruments/FICFacts.cs index c96132eb..fdda0b69 100644 --- a/test/Qwack.Core.Tests/Instruments/FICFacts.cs +++ b/test/Qwack.Core.Tests/Instruments/FICFacts.cs @@ -23,7 +23,7 @@ public class FICFacts [Fact] public void FundingInstrumentCollection() { - var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { new FxForward { SolveCurve = "1.blah" }, new FxForward { SolveCurve = "1.blah" }, @@ -39,7 +39,7 @@ public void FundingInstrumentCollection() [Fact] public void PillarDatesTest() { - var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { new FxForward { SolveCurve = "usd.1blah", PillarDate = DateTime.Today }, new FxForward { SolveCurve = "usd.2blah", PillarDate = DateTime.Today.AddDays(1) }, @@ -57,7 +57,7 @@ public void PillarDatesTest() [Fact] public void ImplySolveStagesTest() { - var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var f = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { new IrSwap { diff --git a/test/Qwack.Models.Tests/Risk/BenchmarkRiskFacts.cs b/test/Qwack.Models.Tests/Risk/BenchmarkRiskFacts.cs index f516f98d..2e85248c 100644 --- a/test/Qwack.Models.Tests/Risk/BenchmarkRiskFacts.cs +++ b/test/Qwack.Models.Tests/Risk/BenchmarkRiskFacts.cs @@ -110,13 +110,13 @@ public void BasicRiskMatrixFacts() Price = 95, PillarDate = _originDate.AddDays(180), }; - var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider) + var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider) { f1, f2 }; - var cube = model.BenchmarkRisk(fic, TestProviderHelper.CurrencyProvider, usd); + var cube = model.BenchmarkRisk(fic, TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider, usd); var riskSum = cube.SumOfAllRows; diff --git a/test/benchmark/Qwack.Curves.Benchmark/SolvingOisBenchmark.cs b/test/benchmark/Qwack.Curves.Benchmark/SolvingOisBenchmark.cs index 0157a0fb..628135a9 100644 --- a/test/benchmark/Qwack.Curves.Benchmark/SolvingOisBenchmark.cs +++ b/test/benchmark/Qwack.Curves.Benchmark/SolvingOisBenchmark.cs @@ -77,7 +77,7 @@ public static void Setup() var ccySwaps = new XccyBasisSwap[oisTenors.Length]; - _instruments = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider); + _instruments = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider); for (var i = 0; i < FRATenors.Length; i++) {