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