Implementing a Configurable Maturity Converter

Atoti FRTB allows developers to implement their own maturity converters based on the IMaturityConverter interface. This page will walk you through the process of implementing a converter than can be configured in a properties file.

The IMaturityConverter interface and the MaturityConverterConfig class

Any implementation of a maturity converter implements the IMaturityConverter interface. These implementations must be autowirable through Spring, as well as usable as an extended plugin. The interface defines two methods:

  • toDate() to convert a String tenor to a LocalDate object
  • toYearFraction() to return the year fraction between a LocalDate as-of-date and a String maturity

The MaturityConverterConfig class is used to expose (as a bean) the implementation of the interface that is used throughout the project. The default implementation is Actual360MaturityConverter

,

based on the Actual/360 day count convention.

The example implementation below does not use the leafCoordinates or pivot parameters defined in the interface, as currently all conversion is handled in the ETL.

Implementing a custom configurable converter

Although the default converter is hard-coded to the Actual/360 convention, you can easily implement a converter that uses the Spring environment to define the day counts.

The static class constants must cover:

  • Defaults for the week, month and year day counts
  • Keys for the Spring environment properties
  • A Map<String, Integer> to allow repeated retrieval of day values after initialisation
public ConfigurableMaturityConverter(Environment env) {
   DAY_VALUES.put(DAY_KEY, 1);
   DAY_VALUES.put(WEEK_KEY, Integer.parseInt(env.getProperty(WEEK_PROPERTY, WEEK_DEFAULT)));
   DAY_VALUES.put(MONTH_KEY, Integer.parseInt(env.getProperty(MONTH_PROPERTY, MONTH_DEFAULT)));
   DAY_VALUES.put(YEAR_KEY, Integer.parseInt(env.getProperty(YEAR_PROPERTY, YEAR_DEFAULT)));
}

Then add the week, month and year values to the application.properties file, which is loaded by default into the Spring environment:

# The number of days in a week, for maturity conversion purposes.
maturity-conversion.days.week=7

# The number of days in a month, for maturity conversion purposes.
maturity-conversion.days.month=30

# The number of days in a year, for maturity conversion purposes.
maturity-conversion.days.year=365

To take advantage of the LocalDate API, the toYearFraction() method converts the String maturity into a LocalDate using the toDate() method, and retrieves the year count directly from the Map initialised above:

@Override
public double toYearFraction(IActivePivot pivot, IDatastoreVersion datastoreVersion, LocalDate asOfDate, String maturity, List<Object> leafCoordinates) {
   LocalDate maturityDate = toDate(pivot, datastoreVersion, asOfDate, maturity, new LocalDateParser(), leafCoordinates);
   Double daysFromAsOfDate = (double)(maturityDate.toEpochDay() - asOfDate.toEpochDay());
   return daysFromAsOfDate / DAY_VALUES.get(YEAR_KEY);
}

The toDate() method has to handle both String representations of dates, as well as tenors. One of the parameters of the method is a LocalDateParser, which can be initialised to use any date format (with the default being yyyy-MM-dd).

@Override
public LocalDate toDate(IActivePivot pivot, IDatastoreVersion datastoreVersion, LocalDate asOfDate, String tenorOrDate, LocalDateParser parser, List<Object> leafCoordinates) {
   LocalDate sensitivityDate;
   try {
      sensitivityDate = parser.parse(tenorOrDate);
   } catch (DateTimeParseException e) {
      // Not a date, mostly a tenor
      sensitivityDate = asOfDate.plusDays(daysInTenor(tenorOrDate));
   }
   return sensitivityDate;
}

In this example, the conversion of the tenor into a number of days is handled in the static daysInTenor method. The example covers the Tomorrow Next tenor, and any combination of a number and one of D,W,M,Y in both uppercase and lowercase. In the FRTB project, tenors expressed as numbers without a period string are assumed to be fractions of a year. Any special cases and error handling must be incorporated in this conversion.

private static int daysInTenor(String tenor) {

   if(tenor.equalsIgnoreCase("TN")) {
      return 2;
   }

   double number = Double.parseDouble(tenor.replaceAll("[^0-9.]", ""));
   String tenorPeriod = String.valueOf(tenor.charAt(tenor.length() - 1));
   tenorPeriod = tenorPeriod.replaceAll("[0-9]", "");
   int days = 0;
   String tenorLowerCase = tenorPeriod.toLowerCase();
   if(!tenorLowerCase.isEmpty() && DAY_VALUES.containsKey(tenorLowerCase)) {
      days = (int) number * DAY_VALUES.get(tenorLowerCase);
   } else {
      //assume it's an frtb vertex expressed in years
      double vertex = Double.parseDouble(tenor);
      days = (int) (vertex * DAY_VALUES.get(YEAR_KEY));
   }
   return days;
}

To use the ConfigurableMaturityConverter, the MaturityConverterConfig class must autowire the environment and initialise the converter, exposing it as a bean:

@Configuration
public class MaturityConverterConfig implements IMaturityConverterConfig {

   @Autowired
   private Environment env;

   /**
    * Returns the maturity converter to be used in the project, as a Spring bean.
    * Default maturity converter is a {@link com.activeviam.frtb.core.dates.impl.Actual360MaturityConverter}.
    * This should be replaced by any custom maturity converter required by the project.
    * @return The required maturity converter.
    */
   @Bean
   public IMaturityConverter maturityConverter() {
      return new ConfigurableMaturityConverter(env);
   }

}