Skip to content

Commit

Permalink
Inflation refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
gavbrennan committed May 24, 2023
1 parent 3fe212e commit dbfb9b6
Show file tree
Hide file tree
Showing 32 changed files with 257 additions and 96 deletions.
2 changes: 1 addition & 1 deletion clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public static object CreateFundingModel(
fxMatrix = fxMatrixCache.GetObject((string)FxMatrix).Value;
}
var emptyCurves = new Dictionary<string, IrCurve>();
var emptyCurves = new Dictionary<string, IIrCurve>();
if (fic != null)
{
emptyCurves = fic.Value.ImplyContainedCurves(BuildDate, Interpolator1DType.LinearFlatExtrap);
Expand Down
10 changes: 1 addition & 9 deletions clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, () =>
Expand All @@ -52,15 +51,8 @@ public static object CreateCPICurveFromForecasts(
return $"Rate index {InfIndex} not found in cache";
}
var fixings = new Dictionary<DateTime, double>();
if(ContainerStores.GetObjectCache<IFixingDictionary>().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,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ public static object CreateFundingInstrumentCollection(
var ficInstruments = Instruments.GetAnyFromCache<FundingInstrumentCollection>()
.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);
Expand Down
2 changes: 1 addition & 1 deletion clients/Qwack.Excel.Next/Risk/RiskFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static object PortfolioIrBenchmarkDelta(
var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName);
var fic = ContainerStores.GetObjectCache<FundingInstrumentCollection>().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);
});
}
Expand Down
2 changes: 1 addition & 1 deletion clients/Qwack.Excel/Curves/IRCurveFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ public static object CreateFundingModel(
fxMatrix = fxMatrixCache.GetObject((string)FxMatrix).Value;
}
var emptyCurves = new Dictionary<string, IrCurve>();
var emptyCurves = new Dictionary<string, IIrCurve>();
if (fic != null)
{
emptyCurves = fic.Value.ImplyContainedCurves(BuildDate, Interpolator1DType.LinearFlatExtrap);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ public static object CreateFundingInstrumentCollection(
var ficInstruments = Instruments.GetAnyFromCache<FundingInstrumentCollection>()
.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);
Expand Down
2 changes: 1 addition & 1 deletion clients/Qwack.Excel/Risk/RiskFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ public static object PortfolioIrBenchmarkDelta(
var model = InstrumentFunctions.GetModelFromCache(ModelName, PortfolioName);
var fic = ContainerStores.GetObjectCache<FundingInstrumentCollection>().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);
});
}
Expand Down
Binary file not shown.
39 changes: 25 additions & 14 deletions src/Qwack.Core/Curves/CPICurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,38 @@ public class CPICurve : IIrCurve
{
public CPICurve() { }

public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, InflationIndex inflationIndex, Dictionary<DateTime, double> 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<DateTime, double>();

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<DateTime, double>(CpiHistory.Where(x => x.Key <= BuildDate).ToDictionary(x => x.Key, x => x.Value));
var allCpiPoints = new Dictionary<DateTime, double>();

for (var i = 0; i < Pillars.Length; i++)
{
Expand All @@ -49,15 +66,14 @@ private void BuildInterpolator()
public DateTime[] Pillars { get; set; } = Array.Empty<DateTime>();
public double[] CpiRates { get; set; } = Array.Empty<double>();

public Dictionary<DateTime, double> CpiHistory { get; set; } = new();

public int NumberOfPillars => Pillars.Length;

public InflationIndex InflationIndex { get; set; }

private IInterpolator1D _cpiInterp;

public int SolveStage { get; set; }
public string CollateralSpec { get; set; }

public IIrCurve BumpRate(int pillarIx, double delta, bool mutate)
{
Expand All @@ -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)
Expand All @@ -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<DateTime, IIrCurve> 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)
{
Expand Down Expand Up @@ -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);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Qwack.Core/Curves/IIrCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ public interface IIrCurve

IIrCurve RebaseDate(DateTime newAnchorDate);

int SolveStage { get; }
int SolveStage { get; set; }

string CollateralSpec { get; set; }
}
}
2 changes: 1 addition & 1 deletion src/Qwack.Core/Curves/IrCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<DateTime, double> Fixings { get; set; }
Expand Down
8 changes: 6 additions & 2 deletions src/Qwack.Core/Curves/SvenssonCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
4 changes: 3 additions & 1 deletion src/Qwack.Core/Instruments/Cashflow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -51,7 +52,8 @@ public CashFlow()
FlowType = FlowType,
Basis = Basis,
RateIndex = RateIndex,
Dcf = Dcf
Dcf = Dcf,
CpiFixingLagInMonths = CpiFixingLagInMonths
};
}

Expand Down
6 changes: 3 additions & 3 deletions src/Qwack.Core/Instruments/CashflowSchedule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
39 changes: 29 additions & 10 deletions src/Qwack.Core/Instruments/Funding/FundingInstrumentCollection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -16,39 +17,57 @@ namespace Qwack.Core.Instruments.Funding
public class FundingInstrumentCollection : List<IFundingInstrument>
{
private readonly ICurrencyProvider _currencyProvider;
private readonly ICalendarProvider _calendarProvider;

public List<string> 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<string, IrCurve> ImplyContainedCurves(DateTime buildDate, Interpolator1DType interpType)
public Dictionary<string, IIrCurve> ImplyContainedCurves(DateTime buildDate, Interpolator1DType interpType)
{
var o = new Dictionary<string, IrCurve>();
var o = new Dictionary<string, IIrCurve>();

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;
}
Expand Down
Loading

0 comments on commit dbfb9b6

Please sign in to comment.