diff --git a/modules/product/src/main/java/com/opengamma/strata/product/index/type/ImmutableOvernightFutureContractSpec.java b/modules/product/src/main/java/com/opengamma/strata/product/index/type/ImmutableOvernightFutureContractSpec.java
index 7195b8747b..f75b212ad2 100644
--- a/modules/product/src/main/java/com/opengamma/strata/product/index/type/ImmutableOvernightFutureContractSpec.java
+++ b/modules/product/src/main/java/com/opengamma/strata/product/index/type/ImmutableOvernightFutureContractSpec.java
@@ -10,8 +10,10 @@
import java.io.Serializable;
import java.time.LocalDate;
+import java.time.Period;
import java.time.YearMonth;
import java.util.NoSuchElementException;
+import java.util.Optional;
import org.joda.beans.ImmutableBean;
import org.joda.beans.JodaBeanUtils;
@@ -70,6 +72,13 @@ public final class ImmutableOvernightFutureContractSpec
*/
@PropertyDefinition(validate = "notNull")
private final DateSequence dateSequence;
+ /**
+ * The accrual period of the future.
+ *
+ * This is the period of time over which the daily interest rate fixing is observed.
+ */
+ @PropertyDefinition(get = "optional")
+ private final Period accrualPeriod;
/**
* The method of accruing Overnight interest.
*/
@@ -150,7 +159,7 @@ private OvernightFutureTrade createTrade(
LocalDate startDate,
ReferenceData refData) {
- LocalDate nextReferenceDate = dateSequence.baseSequence().next(startDate);
+ LocalDate nextReferenceDate = getFinalReferenceDate(startDate);
double accrualFactor = startDate.withDayOfMonth(1).until(nextReferenceDate.withDayOfMonth(1), MONTHS) / 12d;
LocalDate endDate = endDateAdjustment.adjust(nextReferenceDate, refData);
LocalDate lastTradeDate = lastTradeDateAdjustment.adjust(nextReferenceDate, refData);
@@ -191,7 +200,7 @@ private OvernightFuturePosition createPosition(
LocalDate startDate,
ReferenceData refData) {
- LocalDate nextReferenceDate = dateSequence.baseSequence().next(startDate);
+ LocalDate nextReferenceDate = getFinalReferenceDate(startDate);
double accrualFactor = startDate.withDayOfMonth(1).until(nextReferenceDate.withDayOfMonth(1), MONTHS) / 12d;
LocalDate endDate = endDateAdjustment.adjust(nextReferenceDate, refData);
LocalDate lastTradeDate = lastTradeDateAdjustment.adjust(nextReferenceDate, refData);
@@ -216,10 +225,19 @@ public LocalDate calculateReferenceDate(LocalDate tradeDate, SequenceDate sequen
@Override
public LocalDate calculateLastFixingDate(LocalDate referenceDate, ReferenceData refData) {
- LocalDate nextReferenceDate = dateSequence.baseSequence().next(referenceDate);
+ LocalDate nextReferenceDate = getFinalReferenceDate(referenceDate);
return endDateAdjustment.adjust(nextReferenceDate, refData);
}
+ private LocalDate getFinalReferenceDate(LocalDate referenceDate) {
+ if (getAccrualPeriod().isPresent()) {
+ YearMonth finalReferenceMonth = YearMonth.from(referenceDate.plus(getAccrualPeriod().get()));
+ return dateSequence.dateMatching(finalReferenceMonth);
+ } else {
+ return dateSequence.baseSequence().next(referenceDate);
+ }
+ }
+
//-------------------------------------------------------------------------
@Override
public String toString() {
@@ -237,6 +255,7 @@ public String toString() {
"name",
"index",
"dateSequence",
+ "accrualPeriod",
"accrualMethod",
"startDateAdjustment",
"endDateAdjustment",
@@ -246,6 +265,7 @@ public String toString() {
b -> b.getName(),
b -> b.getIndex(),
b -> b.getDateSequence(),
+ b -> b.accrualPeriod,
b -> b.getAccrualMethod(),
b -> b.getStartDateAdjustment(),
b -> b.getEndDateAdjustment(),
@@ -281,6 +301,7 @@ private ImmutableOvernightFutureContractSpec(
String name,
OvernightIndex index,
DateSequence dateSequence,
+ Period accrualPeriod,
OvernightAccrualMethod accrualMethod,
BusinessDayAdjustment startDateAdjustment,
DaysAdjustment endDateAdjustment,
@@ -296,6 +317,7 @@ private ImmutableOvernightFutureContractSpec(
this.name = name;
this.index = index;
this.dateSequence = dateSequence;
+ this.accrualPeriod = accrualPeriod;
this.accrualMethod = accrualMethod;
this.startDateAdjustment = startDateAdjustment;
this.endDateAdjustment = endDateAdjustment;
@@ -343,6 +365,17 @@ public DateSequence getDateSequence() {
return dateSequence;
}
+ //-----------------------------------------------------------------------
+ /**
+ * Gets the accrual period of the future.
+ *
+ * This is the period of time over which the daily interest rate fixing is observed.
+ * @return the optional value of the property, not null
+ */
+ public Optional getAccrualPeriod() {
+ return Optional.ofNullable(accrualPeriod);
+ }
+
//-----------------------------------------------------------------------
/**
* Gets the method of accruing Overnight interest.
@@ -422,6 +455,7 @@ public boolean equals(Object obj) {
return JodaBeanUtils.equal(name, other.name) &&
JodaBeanUtils.equal(index, other.index) &&
JodaBeanUtils.equal(dateSequence, other.dateSequence) &&
+ JodaBeanUtils.equal(accrualPeriod, other.accrualPeriod) &&
JodaBeanUtils.equal(accrualMethod, other.accrualMethod) &&
JodaBeanUtils.equal(startDateAdjustment, other.startDateAdjustment) &&
JodaBeanUtils.equal(endDateAdjustment, other.endDateAdjustment) &&
@@ -437,6 +471,7 @@ public int hashCode() {
hash = hash * 31 + JodaBeanUtils.hashCode(name);
hash = hash * 31 + JodaBeanUtils.hashCode(index);
hash = hash * 31 + JodaBeanUtils.hashCode(dateSequence);
+ hash = hash * 31 + JodaBeanUtils.hashCode(accrualPeriod);
hash = hash * 31 + JodaBeanUtils.hashCode(accrualMethod);
hash = hash * 31 + JodaBeanUtils.hashCode(startDateAdjustment);
hash = hash * 31 + JodaBeanUtils.hashCode(endDateAdjustment);
@@ -454,6 +489,7 @@ public static final class Builder extends DirectFieldsBeanBuilder
+ * This is the period of time over which the daily interest rate fixing is observed.
+ * @param accrualPeriod the new value
+ * @return this, for chaining, not null
+ */
+ public Builder accrualPeriod(Period accrualPeriod) {
+ this.accrualPeriod = accrualPeriod;
+ return this;
+ }
+
/**
* Sets the method of accruing Overnight interest.
* @param accrualMethod the new value, not null
@@ -670,11 +725,12 @@ public Builder notional(double notional) {
//-----------------------------------------------------------------------
@Override
public String toString() {
- StringBuilder buf = new StringBuilder(288);
+ StringBuilder buf = new StringBuilder(320);
buf.append("ImmutableOvernightFutureContractSpec.Builder{");
buf.append("name").append('=').append(JodaBeanUtils.toString(name)).append(',').append(' ');
buf.append("index").append('=').append(JodaBeanUtils.toString(index)).append(',').append(' ');
buf.append("dateSequence").append('=').append(JodaBeanUtils.toString(dateSequence)).append(',').append(' ');
+ buf.append("accrualPeriod").append('=').append(JodaBeanUtils.toString(accrualPeriod)).append(',').append(' ');
buf.append("accrualMethod").append('=').append(JodaBeanUtils.toString(accrualMethod)).append(',').append(' ');
buf.append("startDateAdjustment").append('=').append(JodaBeanUtils.toString(startDateAdjustment)).append(',').append(' ');
buf.append("endDateAdjustment").append('=').append(JodaBeanUtils.toString(endDateAdjustment)).append(',').append(' ');
diff --git a/modules/product/src/main/java/com/opengamma/strata/product/index/type/StandardOvernightFutureContractSpecs.java b/modules/product/src/main/java/com/opengamma/strata/product/index/type/StandardOvernightFutureContractSpecs.java
index e734411a5b..a084767b52 100644
--- a/modules/product/src/main/java/com/opengamma/strata/product/index/type/StandardOvernightFutureContractSpecs.java
+++ b/modules/product/src/main/java/com/opengamma/strata/product/index/type/StandardOvernightFutureContractSpecs.java
@@ -22,6 +22,8 @@
import static com.opengamma.strata.product.swap.OvernightAccrualMethod.AVERAGED_DAILY;
import static com.opengamma.strata.product.swap.OvernightAccrualMethod.COMPOUNDED;
+import java.time.Period;
+
import com.opengamma.strata.basics.date.BusinessDayAdjustment;
import com.opengamma.strata.basics.date.DaysAdjustment;
@@ -183,8 +185,9 @@ final class StandardOvernightFutureContractSpecs {
ImmutableOvernightFutureContractSpec.builder()
.name("USD-SOFR-3M-IMM-CME")
.index(USD_SOFR)
- .dateSequence(QUARTERLY_IMM)
+ .dateSequence(QUARTERLY_IMM_6_SERIAL)
.accrualMethod(COMPOUNDED)
+ .accrualPeriod(Period.ofMonths(3))
.notional(1_000_000d)
.build();
diff --git a/modules/product/src/test/java/com/opengamma/strata/product/index/type/OvernightFutureContractSpecTest.java b/modules/product/src/test/java/com/opengamma/strata/product/index/type/OvernightFutureContractSpecTest.java
index b6bed3210f..4e5aa9b114 100644
--- a/modules/product/src/test/java/com/opengamma/strata/product/index/type/OvernightFutureContractSpecTest.java
+++ b/modules/product/src/test/java/com/opengamma/strata/product/index/type/OvernightFutureContractSpecTest.java
@@ -330,6 +330,21 @@ public void test_createPosition_usdFedFund1mCme() {
assertThat(trade.getProduct().getNotional()).isEqualTo(5_000_000d);
}
+ @Test
+ public void test_createPosition_usdSofr3mCme() {
+ OvernightFutureContractSpec test = OvernightFutureContractSpecs.USD_SOFR_3M_IMM_CME;
+ OvernightFuturePosition trade = test.createPosition(SecurityId.of("OG", "1"), YearMonth.of(2025, 7), 20, REF_DATA);
+ assertThat(trade.getCurrency()).isEqualTo(Currency.USD);
+ assertThat(trade.getQuantity()).isEqualTo(20);
+ assertThat(trade.getProduct().getIndex()).isEqualTo(USD_SOFR);
+ assertThat(trade.getProduct().getAccrualMethod()).isEqualTo(COMPOUNDED);
+ assertThat(trade.getProduct().getAccrualFactor()).isEqualTo(3 / 12d);
+ assertThat(trade.getProduct().getStartDate()).isEqualTo(date(2025, 7, 16));
+ assertThat(trade.getProduct().getEndDate()).isEqualTo(date(2025, 10, 14));
+ assertThat(trade.getProduct().getLastTradeDate()).isEqualTo(date(2025, 10, 14));
+ assertThat(trade.getProduct().getNotional()).isEqualTo(1_000_000d);
+ }
+
//-------------------------------------------------------------------------
public static Object[][] data_name() {
return new Object[][] {