/// <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 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> /// 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())); }