public void GetPPDTest() { string[] rows = new string[] { "1 MONTH", "2 MONTH", "3 MONTH", "6 MONTH", "1 YEAR", "2 YEAR", "3 YEAR", "5 YEAR", "7 YEAR", "10 YEAR" }; string[] cols = new string[] { "1YEAR", "2 YEAR", "3 YEAR", "4 YEAR", "5 YEAR", "6 YEAR", "7 YEAR", "8 YEAR", "9 YEAR", "10 YEAR" }; object[][] data = new object[][] { new object[] { 6.4M, 6.4M, 6.6M, 7M, 7M, 7M, 6.9M, 6.9M, 6.9M, 6.8M }, new object[] { 6.4M, 6.4M, 6.6M, 7M, 7M, 7M, 6.9M, 6.9M, 6.9M, 6.8M }, new object[] { 6.5M, 6.6M, 6.7M, 6.8M, 6.8M, 6.8M, 6.7M, 6.7M, 6.7M, 6.6M }, new object[] { 6.5M, 6.6M, 6.7M, 6.8M, 6.8M, 6.8M, 6.7M, 6.7M, 6.7M, 6.5M }, new object[] { 6.5M, 6.5M, 6.4M, 6.35M, 6.4M, 6.4M, 6.3M, 6.3M, 6.3M, 6.2M }, new object[] { 6.4M, 6.4M, 6.2M, 6.15M, 6.1M, 6.1M, 6M, 6M, 6M, 5.9M }, new object[] { 6.1M, 6.25M, 5.8M, 5.55M, 5.3M, 5.3M, 5.2M, 5.2M, 5.2M, 5.1M }, new object[] { 5.7M, 5.4M, 5.2M, 5M, 4.9M, 4.9M, 4.85M, 4.85M, 4.85M, 4.75M }, new object[] { 5.7M, 5.4M, 5.2M, 5M, 4.9M, 4.9M, 4.85M, 4.85M, 4.85M, 4.75M }, new object[] { 5.7M, 5.4M, 5.2M, 5M, 4.9M, 4.9M, 4.85M, 4.85M, 4.85M, 4.75M } }; SwaptionPPDGrid target = new SwaptionPPDGrid(rows, cols, data); string expiry = "3M"; string tenor = "3Y"; decimal expected = 6.7m; decimal actual = target.GetPPD(expiry, tenor); Assert.AreEqual(expected, actual); expiry = "2Y"; tenor = "5Y"; expected = 6.1m; actual = target.GetPPD(expiry, tenor); Assert.AreEqual(expected, actual); }
public string PublishLpmSwaptionVolMatrix(object[][] structurePropertiesRange, object[][] publishPropertiesRange, object[][] valuesRange, object[][] rateCurveFiltersRange) { NamedValueSet structureProperties = structurePropertiesRange.ToNamedValueSet(); object[,] values = valuesRange.ConvertArrayToMatrix(); string[] tenors = Array.ConvertAll(values.GetRow(0).Skip(1).ToArray(), Convert.ToString); string[] expiries = Array.ConvertAll(values.GetColumn(0).Skip(1).ToArray(), Convert.ToString); object[][] dataObjects = values.GetRows(1, values.RowCount(), 1, values.ColumnCount()); object[][] data = dataObjects.Select(a => a.Select(b => b).ToArray()).ToArray(); // Setup base values needed to build var baseDate = structureProperties.GetValue <DateTime>(CurveProp.BaseDate, false); if (baseDate == DateTime.MinValue) { baseDate = structureProperties.GetValue <DateTime>(CurveProp.BuildDateTime, false).Date; if (baseDate == DateTime.MinValue) { baseDate = DateTime.Today; } } string sourceName = structureProperties.GetString("Source", true); string currency = structureProperties.GetString(CurveProp.Currency1, true); string marketName = structureProperties.GetString(CurveProp.MarketAndDate, true); string indexName = structureProperties.GetString(CurveProp.IndexName, true); int year = baseDate.Year; int weekOfYear = CultureInfo.CurrentCulture.Calendar.GetWeekOfYear(baseDate, CalendarWeekRule.FirstDay, DayOfWeek.Monday); string id = $"LPMSwaptionCurve.{currency}.{baseDate:yyyy-MM-dd}"; var ppdGrid = new SwaptionPPDGrid(expiries, tenors, data); // Load underlying curve Market market = GetCurve(Logger.Target, Cache, NameSpace, rateCurveFiltersRange); Market matrix = LPMSwaptionCurve.ProcessSwaption(Logger.Target, Cache, market, ppdGrid, id, NameSpace); string newId = $"{marketName}.{indexName}.Swaption.{currency}.{sourceName}.{year}.Week{weekOfYear}"; string name = $"Swaption-{currency}-{sourceName}-{baseDate:dd/MM/yyyy}"; structureProperties.Set("Identifier", newId); structureProperties.Set("Name", name); // Save Publish(matrix, "Market." + newId, structureProperties, publishPropertiesRange); return(newId); }
/// <summary> /// Process a PPD Grid. The result is a Market structure that can be published. /// </summary> /// <param name="logger">The logger</param> /// <param name="cache">The cache.</param> /// <param name="swapCurve">The latest rate curve</param> /// <param name="ppdGrid">The raw Points Per Day matrix supplied from the subscriber</param> /// <param name="id">The id to use in publishing the curve</param> /// <param name="nameSpace">The client namespace</param> /// <returns></returns> public static Market ProcessSwaption(ILogger logger, ICoreCache cache, Market swapCurve, SwaptionPPDGrid ppdGrid, string id, string nameSpace) { var mkt = swapCurve; var curve = new SimpleRateCurve(mkt); // List the values so we can build our ATM vols var atmVols = new Dictionary <SimpleKey, decimal>(); // Create a calendar to use to modify the date // default to be Sydney... IBusinessCalendar bc = BusinessCenterHelper.ToBusinessCalendar(cache, new[] { "AUSY" }, nameSpace); //BusinessCalendarHelper("AUSY"); // Use some logic to get the spot date to use // LPM Spot lag is 2 days (mod following) DateTime spotDate = curve.GetSpotDate(); // Extract each surface and build an ATM engine therefrom // Build a list of all possible engines foreach (string e in ExpiryKeys) { // Assume frequency = 4 months until 3 years tenor is reached Period expiration = PeriodHelper.Parse(e); double expiryYearFraction = expiration.ToYearFraction(); foreach (string t in TenorKeys) { // Create a Swap rate for each expiry/tenor pair // Assume frequency = 4 months until 3 years tenor is reached double tenorYearFraction = PeriodHelper.Parse(t).ToYearFraction(); int frequency = tenorYearFraction < 4 ? 4 : 2; // Calculation date // Discount factors // Offsets (elapsed days) var rates = new SwapRate(logger, cache, nameSpace, "AUSY", curve.BaseDate, "ACT/365.FIXED", curve.GetDiscountFactors(), curve.GetDiscountFactorOffsets(), frequency, BusinessDayConventionEnum.MODFOLLOWING); // Calculate the volatility given PPD and swap curve DateTime expiry = bc.Roll(expiration.Add(spotDate), BusinessDayConventionEnum.FOLLOWING); decimal vol = CalculateAtmVolatility(rates, expiry, ppdGrid, expiryYearFraction, tenorYearFraction); atmVols.Add(new SimpleKey(e, t), vol); } } var vols = new object[atmVols.Count + 1, 3]; var i = 1; vols[0, 0] = "Expiry"; vols[0, 1] = "Tenor"; vols[0, 2] = "0"; foreach (var key in atmVols.Keys) { vols[i, 0] = key.Expiry; vols[i, 1] = key.Tenor; vols[i, 2] = atmVols[key]; i++; } DateTime buildDateTime = swapCurve.Items1[0].buildDateTime; var volSurface = new VolatilitySurface(vols, new VolatilitySurfaceIdentifier(id), curve.BaseDate, buildDateTime); return(CreateMarketDocument(volSurface.GetFpMLData())); }
/// <summary> /// Calculate an ATM volatility given a Price Per Day (PPD) value and a swap curve /// The curve provides forward rates for the calculation /// </summary> private static decimal CalculateAtmVolatility(SwapRate rate, DateTime baseDate, SwaptionPPDGrid grid, double expiryYearFraction, double tenorYearFraction) { // Extract the forward rate var swapRate = rate.ComputeSwapRate(baseDate, tenorYearFraction); swapRate = swapRate * 100; // Extract the correct ppd from the grid (compensate for the swap rate being * 100) var ppd = grid.GetPPD(expiryYearFraction, tenorYearFraction) * 0.01m; // Calculate the volatility from the parameters var atmVolatility = (ppd * (decimal)System.Math.Sqrt(250.0)) / swapRate; return(atmVolatility); }