public void GetHeadersTest() { CapFloorATMMatrix target = new CapFloorATMMatrix(_header, _data, _settings, _valuationDate, _id); string[] expected = _header; string[] actual = target.GetHeaders(); for (int row = 0; row < expected.Length; row++) { Assert.AreEqual(expected[row], actual[row], string.Format("Failure at row: {0}.", row)); } }
public void GetVolatilitiesTest() { CapFloorATMMatrix target = new CapFloorATMMatrix(_header, _data, _settings, _valuationDate, _id); decimal[][] actual = target.GetVolatilities(); for (int row = 0; row < _testVols.Length; row++) { decimal expected = _testVols[row][0]; Assert.AreEqual(expected, actual[row][0], string.Format("Failure at row {0}.", row)); } }
public string PublishLpmCapFloorVolMatrix(object[][] structurePropertiesRange, object[][] publishPropertiesRange, object[][] valuesRange, object[][] rateCurveFiltersRange) { // Translate into useful objects NamedValueSet structureProperties = structurePropertiesRange.ToNamedValueSet(); object[,] values = valuesRange.ConvertArrayToMatrix(); string[] columnNames = Array.ConvertAll(values.GetRow(0), Convert.ToString); object[] ppd = values.GetColumn(1); for (int index = 0; index < ppd.Length; index++) { ppd[index] = ppd[index] is Double?Convert.ToDouble(ppd[index]) / 100 : ppd[index]; } values.SetColumn(1, ppd); object[][] data = values.GetRows(1, values.RowCount()); // Create matrix 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 = $"LPMCapFloorCurve.{currency}.{baseDate:yyyy-MM-dd}"; var matrix = new CapFloorATMMatrix(columnNames, data, structurePropertiesRange, baseDate, id); // Load underlying curve Market market = GetCurve(Logger.Target, Cache, NameSpace, rateCurveFiltersRange); // Create the capFloors and properties Market capFloors = LPMCapFloorCurve.ProcessCapFloor(Logger.Target, Cache, NameSpace, market, matrix); string newId = $"{marketName}.{indexName}.CapFloor.{currency}.{sourceName}.{year}.Week{weekOfYear}"; string name = $"CapFloor-{currency}-{sourceName}-{baseDate:dd/MM/yyyy}"; structureProperties.Set("Identifier", newId); structureProperties.Set("Name", name); // Save Publish(capFloors, "Market." + newId, structureProperties, publishPropertiesRange); return(newId); }
public void GetVolatilitySettingsTest() { CapFloorATMMatrix target = new CapFloorATMMatrix(_header, _data, _settings, _valuationDate, _id); object[][] expected = _settings; object[][] actual = target.GetVolatilitySettings(); int i = expected[0][0].ToString().ToLower() == "settings" ? 1 : 0; for (int row = 0 + i; row < expected.Length; row++) { for (int column = 0; column < expected[row].Length; column++) { Assert.AreEqual(expected[row][column].ToString(), actual[row - i][column].ToString(), string.Format("Failure at row: {0}.", row)); } } }
/// <summary> /// Convert the cap/floor PPD values to an ATM parVols structure /// This method then calls the bootstrapper <see cref="ProcessCapFloorPpd"/> /// to generate the capvol curve /// </summary> /// <param name="logger">The logger</param> /// <param name="cache">The cache.</param> /// <param name="rateCurve"></param> /// <param name="capFloor"></param> /// <param name="nameSpace"></param> /// <returns></returns> private static Market ProcessCapFloorPpd(ILogger logger, ICoreCache cache, string nameSpace, Market rateCurve, CapFloorATMMatrix capFloor) { var expiry = capFloor.GetExpiries(); var vols = capFloor.GetVolatilities(); var mkt = rateCurve; var curve = new SimpleRateCurve(mkt); var rawOffsets = curve.GetDiscountFactorOffsets(); var offsets = Array.ConvertAll(rawOffsets, IntToDouble); var discountFactors = curve.GetDiscountFactors(); var volType = capFloor.GetVolatilityTypes(); var atmVols = new Dictionary <string, decimal>(); var settings = CreateCapFloorProperties(capFloor.GetVolatilitySettings()); var bc = BusinessCenterHelper.ToBusinessCalendar(cache, new[] { "AUSY" }, nameSpace); // Use some logic to get the spot date to use // Step through each vol and convert ppd to ATM vol for (var i = 0; i < expiry.Length; i++) { // Create a Swaprate for each expiry // Assume frequency = 4 months until 4 years tenor is reached Period tv = PeriodHelper.Parse(expiry[i]); //double tvYearFraction = tv.ToYearFraction(); //int frequency = tvYearFraction < 4 ? 4 : 2; const int frequency = 4; var rates = new SwapRate(logger, cache, nameSpace, "AUSY", curve.GetBaseDate(), "ACT/365.FIXED", discountFactors, curve.GetDiscountFactorOffsets(), frequency, BusinessDayConventionEnum.MODFOLLOWING); switch (volType[i]) { case "ETO": { DateTime spotDate = settings.GetValue("Calculation Date", DateTime.MinValue); var rollConvention = settings.GetValue("RollConvention", BusinessDayConventionEnum.MODFOLLOWING); DateTime etoDate = bc.Roll(tv.Add(spotDate), rollConvention); atmVols[expiry[i]] = CalculateATMVolatility(settings, spotDate, etoDate, offsets, discountFactors, vols[i][0]); } break; case "Cap/Floor": { DateTime spotDate = settings.GetValue("Calculation Date", DateTime.MinValue); var rollConvention = settings.GetValue("RollConvention", BusinessDayConventionEnum.MODFOLLOWING); DateTime expiryDate = bc.Roll(tv.Add(spotDate), rollConvention); string tenor = DateToTenor(spotDate, expiryDate); double tenorYearFraction = PeriodHelper.Parse(tenor).ToYearFraction(); // Add the caplet maturity to the expiry and then calculate the vol atmVols[expiry[i]] = CalculateCAPATMVolatility(rates, spotDate, tenorYearFraction, vols[i][0]); } break; } } // Fudge to switch the PPD header to ATM // We've converted so we want the new name var headers = capFloor.GetHeaders(); for (var i = 0; i < headers.Length; i++) { if (!headers[i].Contains("PPD")) { continue; } headers[i] = "ATM"; break; } // Convert our lovely dictionary to a grubby array of arrays var volatilities = new object[atmVols.Keys.Count][]; var row = 0; foreach (var key in atmVols.Keys) { volatilities[row] = new object[3]; volatilities[row][0] = key; volatilities[row][1] = atmVols[key]; volatilities[row][2] = volType[row++]; } var convertedCapFloor = new CapFloorATMMatrix(headers, volatilities, capFloor.GetVolatilitySettings(), capFloor.baseDate.Value, capFloor.id); return(ProcessCapFloorATM(logger, cache, nameSpace, rateCurve, convertedCapFloor)); }
/// <summary> /// Process a capfloor curve. If it is an ATM curve bootstrap and publish /// If it is a PPD curve convert to ATM and process as an ATM curve /// </summary> /// <param name="logger">The logger</param> /// <param name="cache">The cache.</param> /// <param name="rateCurve"></param> /// <param name="capFloor"></param> /// <param name="nameSpace"></param> /// <returns></returns> public static Market ProcessCapFloor(ILogger logger, ICoreCache cache, string nameSpace, Market rateCurve, CapFloorATMMatrix capFloor) { return(capFloor.MatrixType == "ATM" ? ProcessCapFloorATM(logger, cache, nameSpace, rateCurve, capFloor) : ProcessCapFloorPpd(logger, cache, nameSpace, rateCurve, capFloor)); }
/// <summary> /// Process a CapFloor ATM parvVols structure. /// The process Bootstraps the parVols using the supplied ratecurve /// </summary> /// <param name="logger">The logger</param> /// <param name="cache">The cache.</param> /// <param name="nameSpace"></param> /// <param name="rateCurve"></param> /// <param name="capFloor"></param> /// <returns></returns> private static Market ProcessCapFloorATM(ILogger logger, ICoreCache cache, string nameSpace, Market rateCurve, CapFloorATMMatrix capFloor) { var id = capFloor.id; var expiry = capFloor.GetExpiries(); var vols = capFloor.GetVolatilities(); var mkt = rateCurve; var curve = new SimpleRateCurve(mkt); var discountFactorDates = curve.GetDiscountFactorDates(); var discountFactors = curve.GetDiscountFactors(); var volType = capFloor.GetVolatilityTypes(); var atmVols = new Dictionary <string, decimal>(); var settings = CreateCapFloorProperties(capFloor.GetVolatilitySettings()); // Create an internal matrix object from the raw data var matrix = new CapFloorVolatilityMatrix(id, expiry, volType, vols, null, discountFactorDates, ArrayUtilities.ArrayToDecimal(discountFactors)); // Create an ATM engine from the matrix var engines = CreateEngines(logger, cache, nameSpace, id, matrix, settings); // Add the default interpolation to use const ExpiryInterpolationType volatilityInterpolation = ExpiryInterpolationType.Linear; if (engines != null) { var vol = new CapletExpiryInterpolatedVolatility(engines[0], volatilityInterpolation); // List the values so we can build our ATM vols var keys = ExpiryKeys.Split(','); // Create a calendar to use to modify the date var businessCalendar = settings.GetValue("BusinessCalendar", "AUSY"); var bc = BusinessCenterHelper.ToBusinessCalendar(cache, new[] { businessCalendar }, nameSpace); // Use some logic to get the spot date to use // LPM Spot lag is 2 days (modfollowing) var spotDate = curve.GetSpotDate(); var rollConvention = settings.GetValue("RollConvention", BusinessDayConventionEnum.FOLLOWING); spotDate = spotDate == curve.GetBaseDate() ? bc.Roll(spotDate.Add(new TimeSpan(2, 0, 0, 0)), rollConvention) : spotDate; //// Extract each surface and build an ATM engine therefrom //// Build a list of all possible engines foreach (var key in keys) { // Calculate the volatility for each target key var tv = PeriodHelper.Parse(key); var target = tv.period == PeriodEnum.D ? bc.Roll(spotDate.AddDays(Convert.ToInt32(tv.periodMultiplier)), rollConvention) : bc.Roll(spotDate.AddMonths(Convert.ToInt32(tv.periodMultiplier)), rollConvention); atmVols.Add(key, vol.ComputeCapletVolatility(target)); } } var outputVols = new object[atmVols.Count + 1, 2]; var i = 1; //Expiry 0 outputVols[0, 0] = "Expiry"; outputVols[0, 1] = "0"; foreach (var key in atmVols.Keys) { outputVols[i, 0] = key; outputVols[i, 1] = atmVols[key]; i++; } DateTime buildDateTime = rateCurve.Items1[0].buildDateTime; var volSurface = new VolatilitySurface(outputVols, new VolatilitySurfaceIdentifier(id), curve.BaseDate, buildDateTime); return(CreateMarketDocument(volSurface.GetFpMLData())); }