diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/date/MarketTenor.java b/modules/basics/src/main/java/com/opengamma/strata/basics/date/MarketTenor.java index 84eebeff84..500313e2e0 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/date/MarketTenor.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/date/MarketTenor.java @@ -108,6 +108,48 @@ public static MarketTenor ofSpot(Tenor tenor) { return new MarketTenor(tenor.toString(), tenor, MARKET_CONVENTION_LAG); } + /** + * Obtains an instance from a number of days from spot. + *

+ * A tenor of 1D will return SN. + * A tenor of 7D will return SW. + * + * @param days the tenor in days + * @return the tenor + */ + public static MarketTenor ofSpotDays(int days) { + if (days == 1) { + return MarketTenor.SN; + } + if (days == 7) { + return MarketTenor.SW; + } + Tenor tenor = Tenor.ofDays(days); + return new MarketTenor(tenor.toString(), tenor, MARKET_CONVENTION_LAG); + } + + /** + * Obtains an instance from a number of months from spot. + * + * @param months the tenor in months + * @return the tenor + */ + public static MarketTenor ofSpotMonths(int months) { + Tenor tenor = Tenor.ofMonths(months); + return new MarketTenor(tenor.toString(), tenor, MARKET_CONVENTION_LAG); + } + + /** + * Obtains an instance from a number of years from spot. + * + * @param years the tenor in years + * @return the tenor + */ + public static MarketTenor ofSpotYears(int years) { + Tenor tenor = Tenor.ofYears(years); + return new MarketTenor(tenor.toString(), tenor, MARKET_CONVENTION_LAG); + } + //------------------------------------------------------------------------- /** * Parses a formatted string representing the market tenor. diff --git a/modules/basics/src/test/java/com/opengamma/strata/basics/date/MarketTenorTest.java b/modules/basics/src/test/java/com/opengamma/strata/basics/date/MarketTenorTest.java index 812422ff89..5b8cd7d915 100644 --- a/modules/basics/src/test/java/com/opengamma/strata/basics/date/MarketTenorTest.java +++ b/modules/basics/src/test/java/com/opengamma/strata/basics/date/MarketTenorTest.java @@ -93,6 +93,29 @@ public void test_ofSpot_special() { assertThat(MarketTenor.ofSpot(TENOR_1D)).isEqualTo(MarketTenor.SN); } + @Test + public void test_ofSpotDays() { + assertThat(MarketTenor.ofSpotDays(1).getCode()).isEqualTo("SN"); + assertThat(MarketTenor.ofSpotDays(7).getCode()).isEqualTo("SW"); + assertThat(MarketTenor.ofSpotDays(3).getCode()).isEqualTo("3D"); + assertThat(MarketTenor.ofSpotDays(20).getCode()).isEqualTo("20D"); + assertThatIllegalArgumentException().isThrownBy(() -> MarketTenor.ofSpotDays(-1)); + } + + @Test + public void test_ofSpotMonths() { + assertThat(MarketTenor.ofSpotMonths(3).getCode()).isEqualTo("3M"); + assertThat(MarketTenor.ofSpotMonths(20).getCode()).isEqualTo("20M"); + assertThatIllegalArgumentException().isThrownBy(() -> MarketTenor.ofSpotDays(-1)); + } + + @Test + public void test_ofSpotYears() { + assertThat(MarketTenor.ofSpotYears(3).getCode()).isEqualTo("3Y"); + assertThat(MarketTenor.ofSpotYears(20).getCode()).isEqualTo("20Y"); + assertThatIllegalArgumentException().isThrownBy(() -> MarketTenor.ofSpotYears(-1)); + } + //------------------------------------------------------------------------- @Test public void test_parse_String_roundTrip() { diff --git a/modules/loader/src/main/java/com/opengamma/strata/loader/LoaderUtils.java b/modules/loader/src/main/java/com/opengamma/strata/loader/LoaderUtils.java index 883fd3f850..60277514ab 100644 --- a/modules/loader/src/main/java/com/opengamma/strata/loader/LoaderUtils.java +++ b/modules/loader/src/main/java/com/opengamma/strata/loader/LoaderUtils.java @@ -5,6 +5,7 @@ */ package com.opengamma.strata.loader; +import static com.opengamma.strata.collect.Guavate.tryCatchToOptional; import static java.time.temporal.ChronoField.HOUR_OF_DAY; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.NANO_OF_SECOND; @@ -34,6 +35,7 @@ import com.opengamma.strata.basics.currency.Currency; import com.opengamma.strata.basics.date.BusinessDayConvention; import com.opengamma.strata.basics.date.DayCount; +import com.opengamma.strata.basics.date.MarketTenor; import com.opengamma.strata.basics.date.Tenor; import com.opengamma.strata.basics.index.FxIndex; import com.opengamma.strata.basics.index.IborIndex; @@ -262,7 +264,7 @@ public static BigDecimal parseBigDecimal(String str) { } /** - * Parses a decimal from the input string, converting it from a percentage to a decimal values. + * Parses a decimal from the input string, converting it from a percentage to a decimal value. *

* If input value is bracketed, it will be parsed as a negative decimal percent. * For e.g. '(12.3456789)' will be parsed as a big decimal -0.123456789. @@ -281,6 +283,27 @@ public static BigDecimal parseBigDecimalPercent(String str) { } } + /** + * Parses a decimal from the input string, converting it from a basis point to a decimal value. + *

+ * If input value is bracketed, it will be parsed as a negative decimal percent. + * For e.g. '(12.3456789)' will be parsed as a big decimal -0.00123456789. + * + * @param str the string to parse + * @return the parsed value + * @throws NumberFormatException if the string cannot be parsed + */ + public static BigDecimal parseBigDecimalBasisPoint(String str) { + try { + return parseBigDecimal(str).movePointLeft(4); + } catch (NumberFormatException ex) { + NumberFormatException nfex = + new NumberFormatException("Unable to parse BigDecimal basis point from '" + str + "'"); + nfex.initCause(ex); + throw nfex; + } + } + private static String normalize(String value) { String normalizedValue = value.trim(); normalizedValue = normalizeIfBracketed(normalizedValue); @@ -479,6 +502,50 @@ public static Period parsePeriod(String str) { } } + /** + * Tries to parse a period from the input string. + * + * @param str the string to parse, may be null + * @return the parsed period, empty if unable to parse + */ + public static Optional tryParsePeriod(String str) { + if (str != null && str.length() >= 2) { + return tryCatchToOptional(() -> parsePeriod(str)); + } + return Optional.empty(); + } + + //------------------------------------------------------------------------- + /** + * Parses a market tenor from the input string. + * + * @param str the string to parse + * @return the parsed value + * @throws IllegalArgumentException if the string cannot be parsed + */ + public static MarketTenor parseMarketTenor(String str) { + try { + return MarketTenor.parse(str); + + } catch (RuntimeException ex) { + throw new IllegalArgumentException("Unknown tenor format: " + str); + } + } + + /** + * Tries to parse a market tenor from the input string. + * + * @param str the string to parse, may be null + * @return the parsed market tenor, empty if unable to parse + */ + public static Optional tryParseMarketTenor(String str) { + if (str != null && str.length() >= 2) { + return tryCatchToOptional(() -> MarketTenor.parse(str)); + } + return Optional.empty(); + } + + //------------------------------------------------------------------------- /** * Parses a tenor from the input string. *

@@ -506,16 +573,13 @@ public static Tenor parseTenor(String str) { * @return the parsed tenor, empty if unable to parse */ public static Optional tryParseTenor(String str) { - if (str != null && str.length() > 1) { - try { - return Optional.of(Tenor.parse(str)); - } catch (RuntimeException ex) { - // ignore - } + if (str != null && str.length() >= 2) { + return tryCatchToOptional(() -> Tenor.parse(str)); } return Optional.empty(); } + //------------------------------------------------------------------------- /** * Parses a frequency from the input string. * @@ -532,6 +596,19 @@ public static Frequency parseFrequency(String str) { } } + /** + * Tries to parse a frequency from the input string. + * + * @param str the string to parse, may be null + * @return the parsed frequency, empty if unable to parse + */ + public static Optional tryParseFrequency(String str) { + if (str != null && !str.isEmpty()) { + return tryCatchToOptional(() -> Frequency.parse(str)); + } + return Optional.empty(); + } + //------------------------------------------------------------------------- /** * Parses currency from the input string. @@ -561,11 +638,7 @@ public static Currency parseCurrency(String str) { */ public static Optional tryParseCurrency(String str) { if (str != null && str.length() == 3 && CURRENCY_MATCHER.matchesAllOf(str)) { - try { - return Optional.of(Currency.parse(str)); - } catch (RuntimeException ex) { - // ignore - } + return tryCatchToOptional(() -> Currency.parse(str)); } return Optional.empty(); } diff --git a/modules/loader/src/test/java/com/opengamma/strata/loader/LoaderUtilsTest.java b/modules/loader/src/test/java/com/opengamma/strata/loader/LoaderUtilsTest.java index 903e420f0c..dace7b3572 100644 --- a/modules/loader/src/test/java/com/opengamma/strata/loader/LoaderUtilsTest.java +++ b/modules/loader/src/test/java/com/opengamma/strata/loader/LoaderUtilsTest.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import com.opengamma.strata.basics.currency.Currency; +import com.opengamma.strata.basics.date.MarketTenor; import com.opengamma.strata.basics.date.Tenor; import com.opengamma.strata.basics.index.FxIndices; import com.opengamma.strata.basics.index.IborIndices; @@ -189,6 +190,21 @@ public void test_parseBigDecimalPercent() { .withMessage("Unable to parse BigDecimal percentage from 'Rubbish'"); } + @Test + public void test_parseBigDecimalBasisPoint() { + assertThat(LoaderUtils.parseBigDecimalBasisPoint("1.2")).isEqualTo(BigDecimal.valueOf(0.00012d)); + assertThat(LoaderUtils.parseBigDecimalBasisPoint("(1.2)")).isEqualTo(BigDecimal.valueOf(-0.00012d)); + assertThatIllegalArgumentException() + .isThrownBy(() -> LoaderUtils.parseBigDecimalBasisPoint("()")) + .withMessage("Unable to parse BigDecimal basis point from '()'"); + assertThatIllegalArgumentException() + .isThrownBy(() -> LoaderUtils.parseBigDecimalBasisPoint("(1.2(3)")) + .withMessage("Unable to parse BigDecimal basis point from '(1.2(3)'"); + assertThatIllegalArgumentException() + .isThrownBy(() -> LoaderUtils.parseBigDecimalBasisPoint("Rubbish")) + .withMessage("Unable to parse BigDecimal basis point from 'Rubbish'"); + } + //------------------------------------------------------------------------- @Test public void test_parseDate_formatter() { @@ -264,6 +280,39 @@ public void test_parsePeriod() { assertThatIllegalArgumentException().isThrownBy(() -> LoaderUtils.parsePeriod("2")); } + @Test + public void test_tryParsePeriod() { + assertThat(LoaderUtils.tryParsePeriod("P2D")).hasValue(Period.ofDays(2)); + assertThat(LoaderUtils.tryParsePeriod("2D")).hasValue(Period.ofDays(2)); + assertThat(LoaderUtils.tryParsePeriod("2X")).isEmpty(); + assertThat(LoaderUtils.tryParsePeriod("2")).isEmpty(); + assertThat(LoaderUtils.tryParsePeriod("")).isEmpty(); + assertThat(LoaderUtils.tryParsePeriod(null)).isEmpty(); + } + + //------------------------------------------------------------------------- + @Test + public void test_parseMarketTenor() { + assertThat(LoaderUtils.parseMarketTenor("P2D")).isEqualTo(MarketTenor.ofSpotDays(2)); + assertThat(LoaderUtils.parseMarketTenor("2D")).isEqualTo(MarketTenor.ofSpotDays(2)); + assertThat(LoaderUtils.parseMarketTenor("ON")).isEqualTo(MarketTenor.ON); + assertThat(LoaderUtils.parseMarketTenor("TN")).isEqualTo(MarketTenor.TN); + assertThatIllegalArgumentException().isThrownBy(() -> LoaderUtils.parseMarketTenor("2")); + } + + @Test + public void test_tryParseMarketTenor() { + assertThat(LoaderUtils.tryParseMarketTenor("P2D")).hasValue(MarketTenor.ofSpotDays(2)); + assertThat(LoaderUtils.tryParseMarketTenor("2D")).hasValue(MarketTenor.ofSpotDays(2)); + assertThat(LoaderUtils.tryParseMarketTenor("ON")).hasValue(MarketTenor.ON); + assertThat(LoaderUtils.tryParseMarketTenor("TN")).hasValue(MarketTenor.TN); + assertThat(LoaderUtils.tryParseMarketTenor("2X")).isEmpty(); + assertThat(LoaderUtils.tryParseMarketTenor("2")).isEmpty(); + assertThat(LoaderUtils.tryParseMarketTenor("")).isEmpty(); + assertThat(LoaderUtils.tryParseMarketTenor(null)).isEmpty(); + } + + //------------------------------------------------------------------------- @Test public void test_parseTenor() { assertThat(LoaderUtils.parseTenor("P2D")).isEqualTo(Tenor.ofDays(2)); @@ -281,6 +330,7 @@ public void test_tryParseTenor() { assertThat(LoaderUtils.tryParseTenor(null)).isEmpty(); } + //------------------------------------------------------------------------- @Test public void test_parseFrequency() { assertThat(LoaderUtils.parseFrequency("P2D")).isEqualTo(Frequency.ofDays(2)); @@ -292,6 +342,20 @@ public void test_parseFrequency() { assertThatIllegalArgumentException().isThrownBy(() -> LoaderUtils.parseFrequency("2")); } + @Test + public void test_tryParseFrequency() { + assertThat(LoaderUtils.tryParseFrequency("P2D")).hasValue(Frequency.ofDays(2)); + assertThat(LoaderUtils.tryParseFrequency("2D")).hasValue(Frequency.ofDays(2)); + assertThat(LoaderUtils.tryParseFrequency("TERM")).hasValue(Frequency.TERM); + assertThat(LoaderUtils.tryParseFrequency("T")).hasValue(Frequency.TERM); + assertThat(LoaderUtils.tryParseFrequency("0T")).hasValue(Frequency.TERM); + assertThat(LoaderUtils.tryParseFrequency("1T")).hasValue(Frequency.TERM); + assertThat(LoaderUtils.tryParseFrequency("2X")).isEmpty(); + assertThat(LoaderUtils.tryParseFrequency("2")).isEmpty(); + assertThat(LoaderUtils.tryParseFrequency("")).isEmpty(); + assertThat(LoaderUtils.tryParseFrequency(null)).isEmpty(); + } + //------------------------------------------------------------------------- @Test public void test_parseCurrency() {