Skip to content

Commit

Permalink
Inflation wired up to Excel (working example)
Browse files Browse the repository at this point in the history
  • Loading branch information
gavbrennan committed May 25, 2023
1 parent dbfb9b6 commit 6dead1d
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 18 deletions.
7 changes: 6 additions & 1 deletion clients/Qwack.Excel.Next/Curves/IRCurveFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,8 @@ public static object CreateFundingModel(
[ExcelArgument(Description = "Funding instrument collection")] string FundingInstrumentCollection,
[ExcelArgument(Description = "Curve to solve stage mappings")] object SolveStages,
[ExcelArgument(Description = "Fx matrix object")] object FxMatrix,
[ExcelArgument(Description = "Fx vol surfaces")] object FxVolSurfaces)
[ExcelArgument(Description = "Fx vol surfaces")] object FxVolSurfaces,
[ExcelArgument(Description = "Fixing dictionaries")] object[] Fixings)
{
return ExcelHelper.Execute(_logger, () =>
{
Expand Down Expand Up @@ -251,9 +252,13 @@ public static object CreateFundingModel(
}
}
}
var fixings = Fixings.GetAnyFromCache<IFixingDictionary>();
var model = new FundingModel(BuildDate, emptyCurves.Values.ToArray(), ContainerStores.CurrencyProvider, ContainerStores.CalendarProvider);
foreach (var f in fixings)
model.AddFixingDictionary(f.Name, f);
if (!(FxMatrix is ExcelMissing))
model.SetupFx(fxMatrix);
Expand Down
22 changes: 22 additions & 0 deletions clients/Qwack.Excel.Next/Curves/InflationCurveFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,5 +62,27 @@ public static object CreateCPICurveFromForecasts(
return ObjectName + '¬' + cache.GetObject(ObjectName).Version;
});
}

[ExcelFunction(Description = "Gets a CPI forecast from a curve", Category = CategoryNames.Curves, Name = CategoryNames.Curves + "_" + nameof(GetCpiForecast), IsThreadSafe = false)]
public static object GetCpiForecast(
[ExcelArgument(Description = "Curve object name")] string ObjectName,
[ExcelArgument(Description = "Fixing Date")] DateTime FixingDate,
[ExcelArgument(Description = "Fixing lag in months")] int LagInMonths)
{
return ExcelHelper.Execute(_logger, () =>
{
if (ContainerStores.GetObjectCache<IIrCurve>().TryGetObject(ObjectName, out var curve))
{
if (curve.Value is CPICurve cpi)
{
return cpi.GetForecast(FixingDate, LagInMonths);
}
return $"Curve {ObjectName} is not a CPI Curve";
}
return $"CPI curve {ObjectName} not found in cache";
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,7 @@ public static object CreateFundingInstrumentCollection(
var loanDepos = Instruments.GetAnyFromCache<FixedRateLoanDeposit>();
var ctgoSwaps = Instruments.GetAnyFromCache<ContangoSwap>();
var flrDepos = Instruments.GetAnyFromCache<FloatingRateLoanDepo>();
var infSwaps = Instruments.GetAnyFromCache<InflationPerformanceSwap>();
//allows merging of FICs into portfolios
var ficInstruments = Instruments.GetAnyFromCache<FundingInstrumentCollection>()
Expand All @@ -546,6 +547,7 @@ public static object CreateFundingInstrumentCollection(
fic.AddRange(loanDepos);
fic.AddRange(ctgoSwaps);
fic.AddRange(flrDepos);
fic.AddRange(infSwaps);
return ExcelHelper.PushToCache(fic, ObjectName);
});
Expand Down
7 changes: 7 additions & 0 deletions clients/Qwack.Excel.Next/Instruments/InstrumentFunctions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1444,6 +1444,9 @@ public static Portfolio GetPortfolio(object[,] Instruments)
var dntOpts = Instruments.GetAnyFromCache<DoubleNoTouchOption>();
var fltDepos = Instruments.GetAnyFromCache<FloatingRateLoanDepo>();

var infSwaps = Instruments.GetAnyFromCache<InflationPerformanceSwap>();
var infSwapsB = Instruments.GetAnyFromCache<InflationSwap>();

//allows merging of FICs into portfolios
var ficInstruments = Instruments.GetAnyFromCache<FundingInstrumentCollection>()
.SelectMany(s => s);
Expand Down Expand Up @@ -1485,6 +1488,10 @@ public static Portfolio GetPortfolio(object[,] Instruments)
pf.Instruments.AddRange(dntOpts);
pf.Instruments.AddRange(fltDepos);

pf.Instruments.AddRange(infSwaps);
pf.Instruments.AddRange(infSwapsB);


return pf;
}
}
Expand Down
Binary file modified examples/Qwack - YeildCurveExample (Frozen Data).xlsx
Binary file not shown.
20 changes: 10 additions & 10 deletions src/Qwack.Core/Curves/CPICurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public CPICurve() { }
public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, InflationIndex inflationIndex)
{
BuildDate = buildDate;
Pillars = pillars;
PillarDates = pillars;
CpiRates = cpiRates;
InflationIndex = inflationIndex;
Basis = InflationIndex.DayCountBasis;
Expand All @@ -28,7 +28,7 @@ public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, Infla
public CPICurve(DateTime buildDate, DateTime[] pillars, double[] cpiRates, DayCountBasis cpiBasis, Frequency cpiFixingLag, Calendar cpiCalendar)
{
BuildDate = buildDate;
Pillars = pillars;
PillarDates = pillars;
CpiRates = cpiRates;
InflationIndex = new InflationIndex
{
Expand All @@ -47,9 +47,9 @@ private void BuildInterpolator()
{
var allCpiPoints = new Dictionary<DateTime, double>();

for (var i = 0; i < Pillars.Length; i++)
for (var i = 0; i < PillarDates.Length; i++)
{
allCpiPoints[Pillars[i]] = CpiRates[i];
allCpiPoints[PillarDates[i]] = CpiRates[i];
}
var x = allCpiPoints.OrderBy(x => x.Key).Select(x => x.Key.ToOADate()).ToArray();
var y = allCpiPoints.OrderBy(x => x.Key).Select(x => x.Value).ToArray();
Expand All @@ -63,10 +63,10 @@ private void BuildInterpolator()

public string Name { get; set; }

public DateTime[] Pillars { get; set; } = Array.Empty<DateTime>();
public DateTime[] PillarDates { get; set; } = Array.Empty<DateTime>();
public double[] CpiRates { get; set; } = Array.Empty<double>();

public int NumberOfPillars => Pillars.Length;
public int NumberOfPillars => PillarDates.Length;

public InflationIndex InflationIndex { get; set; }

Expand All @@ -85,14 +85,14 @@ 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);
return new CPICurve(BuildDate, PillarDates, CpiRates.Select((x, ix) => ix == pillarIx ? x + delta : x).ToArray(), InflationIndex);
}
}
public IIrCurve BumpRateFlat(double delta, bool mutate)
{
if (mutate)
{
for (var i = 0; i < Pillars.Length; i++)
for (var i = 0; i < PillarDates.Length; i++)
{
CpiRates[i] += delta;
}
Expand All @@ -101,7 +101,7 @@ public IIrCurve BumpRateFlat(double delta, bool mutate)
}
else
{
return new CPICurve(BuildDate, Pillars, CpiRates.Select(x => x + delta).ToArray(), InflationIndex);
return new CPICurve(BuildDate, PillarDates, CpiRates.Select(x => x + delta).ToArray(), InflationIndex);
}
}
public Dictionary<DateTime, IIrCurve> BumpScenarios(double delta, DateTime lastSensitivityDate) => throw new NotImplementedException();
Expand Down Expand Up @@ -141,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);
return new CPICurve(BuildDate, PillarDates, CpiRates.Select((x, ix) => ix == pillarIx ? rate : x).ToArray(), InflationIndex);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Qwack.Core/Curves/IIrCurve.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public interface IIrCurve
IIrCurve RebaseDate(DateTime newAnchorDate);

int SolveStage { get; set; }

string CollateralSpec { get; set; }
DateTime[] PillarDates { get; }
}
}
15 changes: 12 additions & 3 deletions src/Qwack.Core/Instruments/Funding/InflationPerformanceSwap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,16 @@ public InflationPerformanceSwap(DateTime startDate, Frequency swapTenor, Inflati
public string ForecastCurveCpi { get; set; }
public string DiscountCurve { get; set; }
public string SolveCurve { get; set; }
public DateTime PillarDate { get; set; }

private DateTime? _pillarDate;
public DateTime PillarDate
{
get => _pillarDate ?? EndDate;
set
{
_pillarDate = value;
}
}
public string TradeId { get; set; }
public string Counterparty { get; set; }
public InflationIndex RateIndex { get; set; }
Expand Down Expand Up @@ -169,15 +178,15 @@ public List<CashFlow> ExpectedCashFlows(IAssetFxModel model)

if (InitialFixing == 0)
{
if (!model.TryGetFixingDictionary(ForecastCurveCpi, out var fixingDictionary))
if (!model.FundingModel.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 cpiPerf = forecast / InitialFixing - 1;

var cpiLegFv = cpiPerf * Notional * (SwapType == SwapPayReceiveType.Payer ? 1.0 : -1.0);
var fixedLegFv = FixedFlow;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public void Solve(IFundingModel fundingModel, FundingInstrumentCollection instru
var points = insForCurve.ToDictionary(x => x.PillarDate, x => x.SuggestPillarValue(fundingModel));
for (var i = 0; i < kv.Value.NumberOfPillars; i++)
{
kv.Value.SetRate(i, points[(kv.Value as IrCurve).PillarDates[i]], true);
kv.Value.SetRate(i, points[kv.Value.PillarDates[i]], true);
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/Qwack.Models/Models/AssetProductEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1254,6 +1254,8 @@ public static string TradeType(this IInstrument ins)
OISFuture => "OISFuture",
IrSwap => "IRSwap",
IrBasisSwap => "IRBasisSwap",
InflationPerformanceSwap => "InfPerfSwap",
InflationSwap => "InfSwap",
CashWrapper wrapper => TradeType(wrapper.UnderlyingInstrument),
_ => throw new Exception($"Unable to handle product of type {ins.GetType()}"),
};
Expand Down
14 changes: 12 additions & 2 deletions src/Qwack.Models/Models/FundingModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public FundingModel(TO_FundingModel transportObject, ICurrencyProvider currencyP
{
if (transportObject.VolSurfaces != null)
VolSurfaces = transportObject.VolSurfaces.ToDictionary(x => x.Key, x => x.Value.GetVolSurface(currencyProvider));
_fixings = transportObject.Fixings?.ToDictionary(x => x.Key, x => (IFixingDictionary)new FixingDictionary(x.Value)) ?? new Dictionary<string, IFixingDictionary>();
SetupFx(new FxMatrix(transportObject.FxMatrix, currencyProvider, calendarProvider));
}

Expand Down Expand Up @@ -138,8 +139,12 @@ public IFundingModel Clone()
var returnValue = new FundingModel(BuildDate, Curves.Values.ToArray(), _currencyProvider, _calendarProvider)
{
FxMatrix = FxMatrix,
VolSurfaces = VolSurfaces
VolSurfaces = VolSurfaces,
};

foreach (var kv in _fixings)
returnValue.AddFixingDictionary(kv.Key, kv.Value);

return returnValue;
}

Expand All @@ -152,6 +157,10 @@ public IFundingModel DeepClone(DateTime? newBuildDate = null)

if (FxMatrix != null)
returnValue.SetupFx(FxMatrix.Clone());

foreach (var kv in _fixings)
returnValue.AddFixingDictionary(kv.Key, kv.Value);

return returnValue;
}

Expand Down Expand Up @@ -287,7 +296,8 @@ public TO_FundingModel GetTransportObject() =>
BuildDate = BuildDate,
VolSurfaces = VolSurfaces.ToDictionary(x => x.Key, x => x.Value.GetTransportObject()),
Curves = Curves.ToDictionary(x => x.Key, x => (x.Value as IrCurve).GetTransportObject()),
FxMatrix = ((FxMatrix)FxMatrix).GetTransportObject()
FxMatrix = ((FxMatrix)FxMatrix).GetTransportObject(),
Fixings = _fixings?.ToDictionary(x => x.Key, x => x.Value.GetTransportObject()),
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ public class TO_FundingModel
public DateTime BuildDate { get; set; }
[ProtoMember(7)]
public TO_FxMatrix FxMatrix { get; set; }
[ProtoMember(8)]
public Dictionary<string, TO_FixingDictionary> Fixings { get; set; }

}
}
83 changes: 83 additions & 0 deletions test/Qwack.Core.Tests/Inflation/InflationCurveFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,89 @@ public void TestInflationCurve()
Assert.DoesNotContain(rates, double.IsNaN);
}

[Fact]
public void TestInflationCurve2()
{
var vd = new DateTime(2023, 03, 01);
var usd = TestProviderHelper.CurrencyProvider.GetCurrencySafe("USD");
var usdIrCurve = new ConstantRateIrCurve(0.05, vd, "USD.CURVE", usd)
{
SolveStage = -1,
};

var infIx = new InflationIndex
{
Currency = usd,
DayCountBasis = DayCountBasis.Act360,
DayCountBasisFixed = DayCountBasis.Act360,
FixingInterpolation = Interpolator1DType.Linear,
FixingLag = 1.Months(),
ResetFrequency = 1.Years()
};

var infSwap6m = new InflationPerformanceSwap(vd, 6.Months(), infIx, 0.045, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE")
{
SolveCurve = "USD.CPI"
};
var infSwap1y = new InflationPerformanceSwap(vd, 1.Years(), infIx, 0.05, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE")
{
SolveCurve = "USD.CPI"
};
var infSwap2y = new InflationPerformanceSwap(vd, 2.Years(), infIx, 0.055, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE")
{
SolveCurve = "USD.CPI"
};
var infSwap3y = new InflationPerformanceSwap(vd, 3.Years(), infIx, 0.06, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE")
{
SolveCurve = "USD.CPI"
};
var infSwap5y = new InflationPerformanceSwap(vd, 5.Years(), infIx, 0.08, 1e6, SwapPayReceiveType.Pay, "USD.CPI", "USD.CURVE")
{
SolveCurve = "USD.CPI"
};


var fic = new FundingInstrumentCollection(TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider)
{
infSwap6m,
infSwap1y,
infSwap2y,
infSwap3y,
infSwap5y,
};

var curves = fic.ImplyContainedCurves(vd, Interpolator1DType.Linear);
curves.Add("USD.CURVE",usdIrCurve);

var fixings = new Dictionary<DateTime, double>()
{
{ vd.AddMonths(-1), 110 },
{ vd.AddMonths(-2), 100 },
};

var pillars = new[] { vd.AddYears(1), vd.AddYears(2) };
var rates = new[] { 120.0, 121.0 };

var model = new FundingModel(vd, curves.Values.ToArray(), TestProviderHelper.CurrencyProvider, TestProviderHelper.CalendarProvider);
model.AddFixingDictionary("USD.CPI", new FixingDictionary(fixings));

var S = new NewtonRaphsonMultiCurveSolverStaged
{
JacobianBump = 0.01,
Tollerance = 0.00000001,
MaxItterations = 100,
};

S.Solve(model, fic);

var cpiCurve = curves["USD.CPI"] as CPICurve;
Assert.DoesNotContain(cpiCurve.CpiRates, double.IsNaN);
foreach(var i in fic)
{
Assert.Equal(0, i.Pv(model, false), 8);
}
}

[Fact]
public void TestInflationPerfSwap()
{
Expand Down

0 comments on commit 6dead1d

Please sign in to comment.