From 90d633747e7cfcb68ae22d36c86ed35fca39fb31 Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Tue, 16 Jun 2020 14:02:32 +0100 Subject: [PATCH] Fix DaysAdjustement.ofBusinessDays Ensure that `ofBusinessDays()` always returns a business day Fixes case where input is a holiday and days to add is zero --- .../strata/basics/date/DaysAdjustment.java | 8 +++++++ .../basics/date/DaysAdjustmentTest.java | 22 +++++++++++++++++++ .../FixedOvernightSwapConventionsTest.java | 8 +++++++ 3 files changed, 38 insertions(+) diff --git a/modules/basics/src/main/java/com/opengamma/strata/basics/date/DaysAdjustment.java b/modules/basics/src/main/java/com/opengamma/strata/basics/date/DaysAdjustment.java index 7c8dbe6b6b..4be4c9b6aa 100644 --- a/modules/basics/src/main/java/com/opengamma/strata/basics/date/DaysAdjustment.java +++ b/modules/basics/src/main/java/com/opengamma/strata/basics/date/DaysAdjustment.java @@ -5,6 +5,8 @@ */ package com.opengamma.strata.basics.date; +import static com.opengamma.strata.basics.date.BusinessDayConventions.FOLLOWING; + import java.io.Serializable; import java.time.LocalDate; import java.util.Map; @@ -149,12 +151,18 @@ public static DaysAdjustment ofCalendarDays(int numberOfDays, BusinessDayAdjustm * This is equivalent to repeatedly finding the next business day. *

* No business day adjustment is applied to the result of the addition. + *

+ * If the input is a holiday, the first business day counted will be the next business day. + * If the input is a holiday and the number of days to add is zero, the result will be the next business day. * * @param numberOfDays the number of days * @param holidayCalendar the calendar that defines holidays and business days * @return the days adjustment */ public static DaysAdjustment ofBusinessDays(int numberOfDays, HolidayCalendarId holidayCalendar) { + if (numberOfDays == 0) { + return new DaysAdjustment(0, HolidayCalendarIds.NO_HOLIDAYS, BusinessDayAdjustment.of(FOLLOWING, holidayCalendar)); + } return new DaysAdjustment(numberOfDays, holidayCalendar, BusinessDayAdjustment.NONE); } diff --git a/modules/basics/src/test/java/com/opengamma/strata/basics/date/DaysAdjustmentTest.java b/modules/basics/src/test/java/com/opengamma/strata/basics/date/DaysAdjustmentTest.java index 9c81ee85e0..d42ea2af91 100644 --- a/modules/basics/src/test/java/com/opengamma/strata/basics/date/DaysAdjustmentTest.java +++ b/modules/basics/src/test/java/com/opengamma/strata/basics/date/DaysAdjustmentTest.java @@ -100,6 +100,14 @@ public void test_ofCalendarDays2_adjust() { assertThat(test.resolve(REF_DATA).adjust(base)).isEqualTo(date(2014, 8, 18)); // Mon } + @Test + public void test_ofCalendarDays2_adjustHoliday() { + DaysAdjustment test = DaysAdjustment.ofCalendarDays(2, BDA_FOLLOW_SAT_SUN); + LocalDate base = date(2014, 8, 16); // Sat + assertThat(test.adjust(base, REF_DATA)).isEqualTo(date(2014, 8, 18)); // Mon + assertThat(test.resolve(REF_DATA).adjust(base)).isEqualTo(date(2014, 8, 18)); // Mon + } + @Test public void test_ofCalendarDays2_null() { assertThatIllegalArgumentException().isThrownBy(() -> DaysAdjustment.ofCalendarDays(2, null)); @@ -175,6 +183,20 @@ public void test_ofBusinessDays3_null() { assertThatIllegalArgumentException().isThrownBy(() -> DaysAdjustment.ofBusinessDays(3, null, null)); } + //------------------------------------------------------------------------- + @Test + public void test_ofBusinessDays0() { + DaysAdjustment test = DaysAdjustment.ofBusinessDays(0, SAT_SUN); + assertThat(test.getDays()).isEqualTo(0); + assertThat(test.getCalendar()).isEqualTo(NO_HOLIDAYS); + assertThat(test.getAdjustment()).isEqualTo(BDA_FOLLOW_SAT_SUN); + assertThat(test.toString()).isEqualTo("0 calendar days then apply Following using calendar Sat/Sun"); + assertThat(test.adjust(date(2014, 8, 15), REF_DATA)).isEqualTo(date(2014, 8, 15)); // Fri + assertThat(test.adjust(date(2014, 8, 16), REF_DATA)).isEqualTo(date(2014, 8, 18)); // Sat -> Mon + assertThat(test.adjust(date(2014, 8, 17), REF_DATA)).isEqualTo(date(2014, 8, 18)); // Sun -> Mon + assertThat(test.adjust(date(2014, 8, 18), REF_DATA)).isEqualTo(date(2014, 8, 18)); // Mon + } + //------------------------------------------------------------------------- @Test public void test_getResultCalendar1() { diff --git a/modules/product/src/test/java/com/opengamma/strata/product/swap/type/FixedOvernightSwapConventionsTest.java b/modules/product/src/test/java/com/opengamma/strata/product/swap/type/FixedOvernightSwapConventionsTest.java index 17e6161615..8e29c0440a 100644 --- a/modules/product/src/test/java/com/opengamma/strata/product/swap/type/FixedOvernightSwapConventionsTest.java +++ b/modules/product/src/test/java/com/opengamma/strata/product/swap/type/FixedOvernightSwapConventionsTest.java @@ -6,6 +6,7 @@ package com.opengamma.strata.product.swap.type; import static com.opengamma.strata.collect.TestHelper.coverPrivateConstructor; +import static com.opengamma.strata.collect.TestHelper.date; import static org.assertj.core.api.Assertions.assertThat; import java.time.LocalDate; @@ -175,6 +176,13 @@ public void test_stub_overnight(FixedOvernightSwapConvention convention, Tenor t assertThat(endDate.isBefore(tradeDate.plus(tenor).plusMonths(1))).isTrue(); } + //------------------------------------------------------------------------- + @Test + public void test_spotFromHoliday() { + FixedOvernightSwapConvention convention = FixedOvernightSwapConventions.GBP_FIXED_TERM_SONIA_OIS; + assertThat(convention.getSpotDateOffset().adjust(date(2020, 6, 14), REF_DATA)).isEqualTo(date(2020, 6, 15)); + } + //------------------------------------------------------------------------- @Test public void coverage() {