/**
* Copyright (C) 2016 - 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.GBP;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static com.opengamma.strata.basics.date.DayCounts.NL_365;
import static com.opengamma.strata.basics.date.HolidayCalendarIds.GBLO;
import static com.opengamma.strata.basics.date.HolidayCalendarIds.USNY;
import static com.opengamma.strata.basics.index.PriceIndices.GB_RPI;
import static com.opengamma.strata.basics.index.PriceIndices.US_CPI_U;
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 com.opengamma.strata.product.bond.CapitalIndexedBondYieldConvention.GB_IL_FLOAT;
import static com.opengamma.strata.product.bond.CapitalIndexedBondYieldConvention.US_IL_REAL;
import static com.opengamma.strata.product.swap.PriceIndexCalculationMethod.INTERPOLATED;
import static org.testng.Assert.assertEquals;
import java.time.LocalDate;
import java.time.Period;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.basics.date.BusinessDayAdjustment;
import com.opengamma.strata.basics.date.BusinessDayConventions;
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.RollConventions;
import com.opengamma.strata.basics.schedule.StubConvention;
import com.opengamma.strata.basics.value.ValueAdjustment;
import com.opengamma.strata.basics.value.ValueSchedule;
import com.opengamma.strata.basics.value.ValueStep;
import com.opengamma.strata.product.SecurityId;
import com.opengamma.strata.product.rate.RateComputation;
import com.opengamma.strata.product.swap.InflationRateCalculation;
/**
* Test {@link CapitalIndexedBond}.
*/
@Test
public class CapitalIndexedBondTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final SecurityId SECURITY_ID = SecurityId.of("OG-Test", "Bond");
private static final SecurityId SECURITY_ID2 = SecurityId.of("OG-Test", "Bond2");
private static final double NOTIONAL = 10_000_000d;
private static final double START_INDEX = 198.475;
private static final double[] COUPONS = new double[] {0.01, 0.015, 0.012, 0.09};
private static final ValueSchedule COUPON;
static {
List<ValueStep> steps = new ArrayList<ValueStep>();
steps.add(ValueStep.of(1, ValueAdjustment.ofReplace(COUPONS[1])));
steps.add(ValueStep.of(2, ValueAdjustment.ofReplace(COUPONS[2])));
steps.add(ValueStep.of(3, ValueAdjustment.ofReplace(COUPONS[3])));
COUPON = ValueSchedule.of(COUPONS[0], steps);
}
private static final InflationRateCalculation RATE_CALC = InflationRateCalculation.builder()
.gearing(COUPON)
.index(US_CPI_U)
.lag(Period.ofMonths(3))
.indexCalculationMethod(INTERPOLATED)
.firstIndexValue(START_INDEX)
.build();
private static final BusinessDayAdjustment EX_COUPON_ADJ =
BusinessDayAdjustment.of(BusinessDayConventions.PRECEDING, USNY);
private static final DaysAdjustment EX_COUPON = DaysAdjustment.ofCalendarDays(-7, EX_COUPON_ADJ);
private static final DaysAdjustment SETTLE_OFFSET = DaysAdjustment.ofBusinessDays(2, USNY);
private static final StandardId LEGAL_ENTITY = StandardId.of("OG-Ticker", "US-Govt");
private static final LocalDate START = LocalDate.of(2008, 1, 13);
private static final LocalDate END = LocalDate.of(2010, 1, 13);
private static final Frequency FREQUENCY = Frequency.P6M;
private static final BusinessDayAdjustment SCHEDULE_ADJ =
BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, USNY);
private static final PeriodicSchedule SCHEDULE =
PeriodicSchedule.of(START, END, FREQUENCY, SCHEDULE_ADJ, StubConvention.NONE, RollConventions.NONE);
public void test_builder_full() {
CapitalIndexedBond test = sut();
assertEquals(test.getSecurityId(), SECURITY_ID);
assertEquals(test.getCurrency(), USD);
assertEquals(test.getDayCount(), ACT_ACT_ISDA);
assertEquals(test.getExCouponPeriod(), EX_COUPON);
assertEquals(test.getLegalEntityId(), LEGAL_ENTITY);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getAccrualSchedule(), SCHEDULE);
assertEquals(test.getRateCalculation(), RATE_CALC);
assertEquals(test.getFirstIndexValue(), RATE_CALC.getFirstIndexValue().getAsDouble());
assertEquals(test.getSettlementDateOffset(), SETTLE_OFFSET);
assertEquals(test.getYieldConvention(), US_IL_REAL);
}
public void test_builder_min() {
CapitalIndexedBond test = CapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.notional(NOTIONAL)
.currency(USD)
.dayCount(ACT_ACT_ISDA)
.rateCalculation(RATE_CALC)
.legalEntityId(LEGAL_ENTITY)
.yieldConvention(US_IL_REAL)
.settlementDateOffset(SETTLE_OFFSET)
.accrualSchedule(SCHEDULE)
.build();
assertEquals(test.getSecurityId(), SECURITY_ID);
assertEquals(test.getCurrency(), USD);
assertEquals(test.getDayCount(), ACT_ACT_ISDA);
assertEquals(test.getExCouponPeriod(), DaysAdjustment.NONE);
assertEquals(test.getLegalEntityId(), LEGAL_ENTITY);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getAccrualSchedule(), SCHEDULE);
assertEquals(test.getRateCalculation(), RATE_CALC);
assertEquals(test.getSettlementDateOffset(), SETTLE_OFFSET);
assertEquals(test.getYieldConvention(), US_IL_REAL);
}
public void test_builder_fail() {
// negative settlement date offset
assertThrowsIllegalArg(() -> CapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.notional(NOTIONAL)
.currency(USD)
.dayCount(ACT_ACT_ISDA)
.rateCalculation(RATE_CALC)
.exCouponPeriod(EX_COUPON)
.legalEntityId(LEGAL_ENTITY)
.yieldConvention(US_IL_REAL)
.settlementDateOffset(DaysAdjustment.ofBusinessDays(-2, USNY))
.accrualSchedule(SCHEDULE)
.build());
// positive ex-coupon days
assertThrowsIllegalArg(() -> CapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.notional(NOTIONAL)
.currency(USD)
.dayCount(ACT_ACT_ISDA)
.rateCalculation(RATE_CALC)
.exCouponPeriod(
DaysAdjustment.ofCalendarDays(7, BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, USNY)))
.legalEntityId(LEGAL_ENTITY)
.yieldConvention(US_IL_REAL)
.settlementDateOffset(SETTLE_OFFSET)
.accrualSchedule(SCHEDULE)
.build());
}
public void test_resolve() {
CapitalIndexedBond base = sut();
LocalDate[] unAdjDates = new LocalDate[] {LocalDate.of(2008, 1, 13), LocalDate.of(2008, 7, 13),
LocalDate.of(2009, 1, 13), LocalDate.of(2009, 7, 13), LocalDate.of(2010, 1, 13)};
CapitalIndexedBondPaymentPeriod[] periodic = new CapitalIndexedBondPaymentPeriod[4];
for (int i = 0; i < 4; ++i) {
LocalDate start = SCHEDULE_ADJ.adjust(unAdjDates[i], REF_DATA);
LocalDate end = SCHEDULE_ADJ.adjust(unAdjDates[i + 1], REF_DATA);
LocalDate detachment = EX_COUPON.adjust(end, REF_DATA);
RateComputation comp = RATE_CALC.createRateComputation(end);
periodic[i] = CapitalIndexedBondPaymentPeriod.builder()
.currency(USD)
.startDate(start)
.endDate(end)
.unadjustedStartDate(unAdjDates[i])
.unadjustedEndDate(unAdjDates[i + 1])
.detachmentDate(detachment)
.realCoupon(COUPONS[i])
.rateComputation(comp)
.notional(NOTIONAL)
.build();
}
CapitalIndexedBondPaymentPeriod nominalExp =
periodic[3].withUnitCoupon(periodic[0].getStartDate(), periodic[0].getUnadjustedStartDate());
ResolvedCapitalIndexedBond expected = ResolvedCapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.dayCount(ACT_ACT_ISDA)
.legalEntityId(LEGAL_ENTITY)
.nominalPayment(nominalExp)
.periodicPayments(periodic)
.frequency(SCHEDULE.getFrequency())
.rollConvention(SCHEDULE.calculatedRollConvention())
.settlementDateOffset(SETTLE_OFFSET)
.yieldConvention(US_IL_REAL)
.rateCalculation(base.getRateCalculation())
.build();
assertEquals(base.resolve(REF_DATA), expected);
}
//-------------------------------------------------------------------------
public void coverage() {
coverImmutableBean(sut());
coverBeanEquals(sut(), sut2());
}
public void test_serialization() {
assertSerialization(sut());
}
//-------------------------------------------------------------------------
static CapitalIndexedBond sut() {
return CapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.notional(NOTIONAL)
.currency(USD)
.dayCount(ACT_ACT_ISDA)
.rateCalculation(RATE_CALC)
.exCouponPeriod(EX_COUPON)
.legalEntityId(LEGAL_ENTITY)
.yieldConvention(US_IL_REAL)
.settlementDateOffset(SETTLE_OFFSET)
.accrualSchedule(SCHEDULE)
.build();
}
static CapitalIndexedBond sut1() {
return CapitalIndexedBond.builder()
.securityId(SECURITY_ID)
.notional(NOTIONAL)
.currency(USD)
.dayCount(ACT_ACT_ISDA)
.rateCalculation(RATE_CALC)
.exCouponPeriod(EX_COUPON)
.legalEntityId(LEGAL_ENTITY)
.yieldConvention(GB_IL_FLOAT)
.settlementDateOffset(SETTLE_OFFSET)
.accrualSchedule(SCHEDULE)
.build();
}
static CapitalIndexedBond sut2() {
return CapitalIndexedBond.builder()
.securityId(SECURITY_ID2)
.notional(5.0e7)
.currency(GBP)
.dayCount(NL_365)
.rateCalculation(
InflationRateCalculation.builder()
.index(GB_RPI)
.lag(Period.ofMonths(2))
.indexCalculationMethod(INTERPOLATED)
.firstIndexValue(124.556)
.build())
.exCouponPeriod(EX_COUPON)
.legalEntityId(StandardId.of("OG-Ticker", "US-Govt-1"))
.yieldConvention(GB_IL_FLOAT)
.settlementDateOffset(DaysAdjustment.ofBusinessDays(2, GBLO))
.accrualSchedule(
PeriodicSchedule.of(
START, END, FREQUENCY,
BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, GBLO),
StubConvention.NONE,
RollConventions.NONE))
.build();
}
}