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 aString
tenor to aLocalDate
objecttoYearFraction()
to return the year fraction between aLocalDate
as-of-date and aString
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);
}
}