/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.product.bond;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.basics.date.BusinessDayConventions.FOLLOWING;
import static com.opengamma.strata.basics.date.HolidayCalendarIds.SAT_SUN;
import static com.opengamma.strata.collect.TestHelper.assertSerialization;
import static com.opengamma.strata.collect.TestHelper.assertThrowsIllegalArg;
import static com.opengamma.strata.collect.TestHelper.coverBeanEquals;
import static com.opengamma.strata.collect.TestHelper.coverImmutableBean;
import static org.testng.Assert.assertEquals;
import java.time.LocalDate;
import java.time.Period;
import java.util.Optional;
import org.testng.annotations.Test;
import com.google.common.collect.ImmutableList;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.date.BusinessDayAdjustment;
import com.opengamma.strata.basics.date.DayCount;
import com.opengamma.strata.basics.date.DayCounts;
import com.opengamma.strata.basics.date.DaysAdjustment;
import com.opengamma.strata.basics.schedule.Frequency;
import com.opengamma.strata.basics.schedule.PeriodicSchedule;
import com.opengamma.strata.basics.schedule.StubConvention;
import com.opengamma.strata.basics.value.Rounding;
import com.opengamma.strata.product.SecurityId;
/**
* Test {@link BondFuture}.
*/
@SuppressWarnings("unchecked")
@Test
public class BondFutureTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
// Underlying bonds
private static final StandardId ISSUER_ID = StandardId.of("OG-Ticker", "GOVT1");
private static final FixedCouponBondYieldConvention YIELD_CONVENTION = FixedCouponBondYieldConvention.US_STREET;
private static final double NOTIONAL = 100000d;
private static final DaysAdjustment SETTLEMENT_DAYS = DaysAdjustment.ofBusinessDays(1, SAT_SUN);
private static final DayCount DAY_COUNT = DayCounts.ACT_ACT_ICMA;
private static final BusinessDayAdjustment BUSINESS_ADJUST = BusinessDayAdjustment.of(FOLLOWING, SAT_SUN);
private static final DaysAdjustment EX_COUPON = DaysAdjustment.NONE;
private static final SecurityId SECURITY_ID = SecurityId.of("OG-Test", "BondFuture");
private static final SecurityId SECURITY_ID2 = SecurityId.of("OG-Test", "BondFuture2");
private static final int NB_BOND = 7;
private static final double[] RATE = new double[] {0.01375, 0.02125, 0.0200, 0.02125, 0.0225, 0.0200, 0.0175};
private static final LocalDate[] START_DATE = new LocalDate[] {
LocalDate.of(2010, 11, 30), LocalDate.of(2010, 12, 31), LocalDate.of(2011, 1, 31), LocalDate.of(2008, 2, 29),
LocalDate.of(2011, 3, 31), LocalDate.of(2011, 4, 30), LocalDate.of(2011, 5, 31)};
private static final Period[] BOND_TENOR = new Period[] {
Period.ofYears(5), Period.ofYears(5), Period.ofYears(5), Period.ofYears(8),
Period.ofYears(5), Period.ofYears(5), Period.ofYears(5)};
@SuppressWarnings("unchecked")
private static final FixedCouponBond[] BOND_PRODUCT = new FixedCouponBond[NB_BOND];
private static final ResolvedFixedCouponBond[] RESOLVED_BASKET = new ResolvedFixedCouponBond[NB_BOND];
static {
for (int i = 0; i < NB_BOND; ++i) {
LocalDate endDate = START_DATE[i].plus(BOND_TENOR[i]);
PeriodicSchedule periodSchedule = PeriodicSchedule.of(
START_DATE[i], endDate, Frequency.P6M, BUSINESS_ADJUST, StubConvention.SHORT_INITIAL, false);
FixedCouponBond product = FixedCouponBond.builder()
.securityId(SecurityId.of("OG-Test", "Bond " + i))
.dayCount(DAY_COUNT)
.fixedRate(RATE[i])
.legalEntityId(ISSUER_ID)
.currency(USD)
.notional(NOTIONAL)
.accrualSchedule(periodSchedule)
.settlementDateOffset(SETTLEMENT_DAYS)
.yieldConvention(YIELD_CONVENTION)
.exCouponPeriod(EX_COUPON)
.build();
BOND_PRODUCT[i] = product;
RESOLVED_BASKET[i] = product.resolve(REF_DATA);
}
}
// future specification
private static final Double[] CONVERSION_FACTOR = new Double[] {.8317, .8565, .8493, .8516, .8540, .8417, .8292};
private static final LocalDate LAST_TRADING_DATE = LocalDate.of(2011, 9, 30);
private static final LocalDate FIRST_NOTICE_DATE = LocalDate.of(2011, 8, 31);
private static final LocalDate LAST_NOTICE_DATE = LocalDate.of(2011, 10, 4);
private static final LocalDate FIRST_DELIVERY_DATE = SETTLEMENT_DAYS.adjust(FIRST_NOTICE_DATE, REF_DATA);
private static final LocalDate LAST_DELIVERY_DATE = SETTLEMENT_DAYS.adjust(LAST_NOTICE_DATE, REF_DATA);
private static final Rounding ROUNDING = Rounding.ofDecimalPlaces(3);
//-------------------------------------------------------------------------
public void test_builder_full() {
BondFuture test = BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT)
.conversionFactors(CONVERSION_FACTOR)
.firstNoticeDate(FIRST_NOTICE_DATE)
.firstDeliveryDate(FIRST_DELIVERY_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastDeliveryDate(LAST_DELIVERY_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build();
assertEquals(test.getDeliveryBasket(), ImmutableList.copyOf(BOND_PRODUCT));
assertEquals(test.getConversionFactors(), ImmutableList.copyOf(CONVERSION_FACTOR));
assertEquals(test.getCurrency(), USD);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getFirstNoticeDate(), FIRST_NOTICE_DATE);
assertEquals(test.getLastNoticeDate(), LAST_NOTICE_DATE);
assertEquals(test.getFirstDeliveryDate(), Optional.of(FIRST_DELIVERY_DATE));
assertEquals(test.getLastDeliveryDate(), Optional.of(LAST_DELIVERY_DATE));
assertEquals(test.getLastTradeDate(), LAST_TRADING_DATE);
assertEquals(test.getRounding(), ROUNDING);
}
public void test_builder_noDeliveryDate() {
BondFuture test = BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT)
.conversionFactors(CONVERSION_FACTOR)
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build();
assertEquals(test.getDeliveryBasket(), ImmutableList.copyOf(BOND_PRODUCT));
assertEquals(test.getConversionFactors(), ImmutableList.copyOf(CONVERSION_FACTOR));
assertEquals(test.getCurrency(), USD);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getFirstNoticeDate(), FIRST_NOTICE_DATE);
assertEquals(test.getLastNoticeDate(), LAST_NOTICE_DATE);
assertEquals(test.getFirstDeliveryDate(), Optional.empty());
assertEquals(test.getLastDeliveryDate(), Optional.empty());
assertEquals(test.getLastTradeDate(), LAST_TRADING_DATE);
assertEquals(test.getRounding(), ROUNDING);
}
public void test_builder_fail() {
// wrong size
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT[0])
.conversionFactors(CONVERSION_FACTOR)
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
// first notice date missing
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT)
.conversionFactors(CONVERSION_FACTOR)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
// last notice date missing
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT)
.conversionFactors(CONVERSION_FACTOR)
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
// basket list empty
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.conversionFactors(CONVERSION_FACTOR)
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
// notional mismatch
FixedCouponBond bond0 = BOND_PRODUCT[0];
FixedCouponBond bond1 = bond0.toBuilder().notional(100).build();
FixedCouponBond bond2 = bond0.toBuilder().currency(Currency.CAD).build();
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(bond0, bond1)
.conversionFactors(CONVERSION_FACTOR[0], CONVERSION_FACTOR[1])
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.firstDeliveryDate(FIRST_DELIVERY_DATE)
.lastDeliveryDate(LAST_DELIVERY_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
// currency mismatch
assertThrowsIllegalArg(() -> BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(bond0, bond2)
.conversionFactors(CONVERSION_FACTOR[0], CONVERSION_FACTOR[1])
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.firstDeliveryDate(FIRST_DELIVERY_DATE)
.lastDeliveryDate(LAST_DELIVERY_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build());
}
//-------------------------------------------------------------------------
public void test_resolve() {
ResolvedBondFuture expected = ResolvedBondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(RESOLVED_BASKET)
.conversionFactors(CONVERSION_FACTOR)
.lastTradeDate(LAST_TRADING_DATE)
.firstNoticeDate(FIRST_NOTICE_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.firstDeliveryDate(FIRST_DELIVERY_DATE)
.lastDeliveryDate(LAST_DELIVERY_DATE)
.rounding(ROUNDING)
.build();
assertEquals(sut().resolve(REF_DATA), expected);
}
//-------------------------------------------------------------------------
public void coverage() {
coverImmutableBean(sut());
coverBeanEquals(sut(), sut2());
}
public void serialization() {
assertSerialization(sut());
}
//-------------------------------------------------------------------------
static BondFuture sut() {
return BondFuture.builder()
.securityId(SECURITY_ID)
.deliveryBasket(BOND_PRODUCT)
.conversionFactors(ImmutableList.copyOf(CONVERSION_FACTOR))
.firstNoticeDate(FIRST_NOTICE_DATE)
.firstDeliveryDate(FIRST_DELIVERY_DATE)
.lastNoticeDate(LAST_NOTICE_DATE)
.lastDeliveryDate(LAST_DELIVERY_DATE)
.lastTradeDate(LAST_TRADING_DATE)
.rounding(ROUNDING)
.build();
}
static BondFuture sut2() {
return BondFuture.builder()
.securityId(SECURITY_ID2)
.conversionFactors(0.9187)
.deliveryBasket(BOND_PRODUCT[3])
.firstNoticeDate(FIRST_NOTICE_DATE.plusDays(7))
.lastNoticeDate(LAST_NOTICE_DATE.plusDays(7))
.lastTradeDate(LAST_TRADING_DATE.plusDays(7))
.build();
}
}