/** * Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.basics.index; import static com.opengamma.strata.basics.date.BusinessDayConventions.FOLLOWING; import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_FOLLOWING; import static com.opengamma.strata.basics.date.BusinessDayConventions.PRECEDING; import java.time.LocalTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import com.google.common.collect.ImmutableMap; import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.date.BusinessDayAdjustment; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DaysAdjustment; import com.opengamma.strata.basics.date.HolidayCalendarId; import com.opengamma.strata.basics.date.PeriodAdditionConvention; import com.opengamma.strata.basics.date.PeriodAdditionConventions; import com.opengamma.strata.basics.date.Tenor; import com.opengamma.strata.basics.date.TenorAdjustment; import com.opengamma.strata.collect.io.CsvFile; import com.opengamma.strata.collect.io.CsvRow; import com.opengamma.strata.collect.io.ResourceConfig; import com.opengamma.strata.collect.io.ResourceLocator; import com.opengamma.strata.collect.named.NamedLookup; /** * Loads standard Ibor Index implementations from CSV. * <p> * See {@link IborIndices} for the description of each. */ final class IborIndexCsvLookup implements NamedLookup<IborIndex> { // http://www.opengamma.com/sites/default/files/interest-rate-instruments-and-market-conventions.pdf // LIBOR - http://www.bbalibor.com/technical-aspects/fixing-value-and-maturity // different rules for overnight // EURIBOR - http://www.bbalibor.com/technical-aspects/fixing-value-and-maturity // EURIBOR - http://www.emmi-benchmarks.eu/assets/files/Euribor_code_conduct.pdf // TIBOR - http://www.jbatibor.or.jp/english/public/ /** * The logger. */ private static final Logger log = Logger.getLogger(IborIndexCsvLookup.class.getName()); /** * The singleton instance of the lookup. */ public static final IborIndexCsvLookup INSTANCE = new IborIndexCsvLookup(); // CSV column headers private static final String NAME_FIELD = "Name"; private static final String CURRENCY_FIELD = "Currency"; private static final String ACTIVE_FIELD = "Active"; private static final String DAY_COUNT_FIELD = "Day Count"; private static final String FIXING_CALENDAR_FIELD = "Fixing Calendar"; private static final String OFFSET_DAYS_FIELD = "Offset Days"; private static final String OFFSET_CALENDAR_FIELD = "Offset Calendar"; private static final String EFFECTIVE_DATE_CALENDAR_FIELD = "Effective Date Calendar"; private static final String TENOR_FIELD = "Tenor"; private static final String TENOR_CONVENTION_FIELD = "Tenor Convention"; private static final String FIXING_TIME_FIELD = "FixingTime"; private static final String FIXING_ZONE_FIELD = "FixingZone"; /** * The time formatter. */ private static final DateTimeFormatter TIME_FORMAT = DateTimeFormatter.ofPattern("HH[:mm]", Locale.ENGLISH); /** * The cache by name. */ private static final ImmutableMap<String, IborIndex> BY_NAME = loadFromCsv(); /** * Restricted constructor. */ private IborIndexCsvLookup() { } //------------------------------------------------------------------------- @Override public Map<String, IborIndex> lookupAll() { return BY_NAME; } private static ImmutableMap<String, IborIndex> loadFromCsv() { List<ResourceLocator> resources = ResourceConfig.orderedResources("IborIndexData.csv"); Map<String, IborIndex> map = new HashMap<>(); for (ResourceLocator resource : resources) { try { CsvFile csv = CsvFile.of(resource.getCharSource(), true); for (CsvRow row : csv.rows()) { IborIndex parsed = parseIborIndex(row); map.put(parsed.getName(), parsed); map.putIfAbsent(parsed.getName().toUpperCase(Locale.ENGLISH), parsed); } } catch (RuntimeException ex) { log.log(Level.SEVERE, "Error processing resource as Ibor Index CSV file: " + resource, ex); return ImmutableMap.of(); } } return ImmutableMap.copyOf(map); } private static IborIndex parseIborIndex(CsvRow row) { String name = row.getField(NAME_FIELD); Currency currency = Currency.parse(row.getField(CURRENCY_FIELD)); boolean active = Boolean.parseBoolean(row.getField(ACTIVE_FIELD)); DayCount dayCount = DayCount.of(row.getField(DAY_COUNT_FIELD)); HolidayCalendarId fixingCal = HolidayCalendarId.of(row.getField(FIXING_CALENDAR_FIELD)); int offsetDays = Integer.parseInt(row.getField(OFFSET_DAYS_FIELD)); HolidayCalendarId offsetCal = HolidayCalendarId.of(row.getField(OFFSET_CALENDAR_FIELD)); HolidayCalendarId effectiveCal = HolidayCalendarId.of(row.getField(EFFECTIVE_DATE_CALENDAR_FIELD)); Tenor tenor = Tenor.parse(row.getField(TENOR_FIELD)); PeriodAdditionConvention tenorConvention = PeriodAdditionConvention.of(row.getField(TENOR_CONVENTION_FIELD)); LocalTime time = LocalTime.parse(row.getField(FIXING_TIME_FIELD), TIME_FORMAT); ZoneId zoneId = ZoneId.of(row.getField(FIXING_ZONE_FIELD)); // interpret CSV DaysAdjustment fixingOffset = DaysAdjustment.ofBusinessDays( -offsetDays, offsetCal, BusinessDayAdjustment.of(PRECEDING, fixingCal)).normalized(); DaysAdjustment effectiveOffset = DaysAdjustment.ofBusinessDays( offsetDays, offsetCal, BusinessDayAdjustment.of(FOLLOWING, effectiveCal)).normalized(); BusinessDayAdjustment adj = BusinessDayAdjustment.of( isEndOfMonth(tenorConvention) ? MODIFIED_FOLLOWING : FOLLOWING, effectiveCal); TenorAdjustment tenorAdjustment = TenorAdjustment.of(tenor, tenorConvention, adj); // build result return ImmutableIborIndex.builder() .name(name) .currency(currency) .active(active) .dayCount(dayCount) .fixingCalendar(fixingCal) .fixingDateOffset(fixingOffset) .effectiveDateOffset(effectiveOffset) .maturityDateOffset(tenorAdjustment) .fixingTime(time) .fixingZone(zoneId) .build(); } private static boolean isEndOfMonth(PeriodAdditionConvention tenorConvention) { return tenorConvention.equals(PeriodAdditionConventions.LAST_BUSINESS_DAY) || tenorConvention.equals(PeriodAdditionConventions.LAST_DAY); } }