/**
* 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.EUR;
import static com.opengamma.strata.basics.currency.Currency.GBP;
import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_FOLLOWING;
import static com.opengamma.strata.basics.date.HolidayCalendarIds.EUTA;
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 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.CurrencyAmount;
import com.opengamma.strata.basics.currency.Payment;
import com.opengamma.strata.basics.date.BusinessDayAdjustment;
import com.opengamma.strata.basics.date.BusinessDayConventions;
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.Schedule;
import com.opengamma.strata.basics.schedule.StubConvention;
import com.opengamma.strata.product.SecurityId;
/**
* Test {@link FixedCouponBond}.
*/
@Test
public class FixedCouponBondTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final FixedCouponBondYieldConvention YIELD_CONVENTION = FixedCouponBondYieldConvention.DE_BONDS;
private static final StandardId LEGAL_ENTITY = StandardId.of("OG-Ticker", "BUN EUR");
private static final double NOTIONAL = 1.0e7;
private static final double FIXED_RATE = 0.015;
private static final DaysAdjustment DATE_OFFSET = DaysAdjustment.ofBusinessDays(3, EUTA);
private static final DayCount DAY_COUNT = DayCounts.ACT_365F;
private static final LocalDate START_DATE = LocalDate.of(2015, 4, 12);
private static final LocalDate END_DATE = LocalDate.of(2025, 4, 12);
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 BusinessDayAdjustment BUSINESS_ADJUST =
BusinessDayAdjustment.of(BusinessDayConventions.MODIFIED_FOLLOWING, EUTA);
private static final PeriodicSchedule PERIOD_SCHEDULE = PeriodicSchedule.of(
START_DATE, END_DATE, Frequency.P6M, BUSINESS_ADJUST, StubConvention.SHORT_INITIAL, false);
private static final int EX_COUPON_DAYS = 5;
private static final DaysAdjustment EX_COUPON =
DaysAdjustment.ofBusinessDays(-EX_COUPON_DAYS, EUTA, BUSINESS_ADJUST);
//-------------------------------------------------------------------------
public void test_builder() {
FixedCouponBond test = sut();
assertEquals(test.getSecurityId(), SECURITY_ID);
assertEquals(test.getDayCount(), DAY_COUNT);
assertEquals(test.getFixedRate(), FIXED_RATE);
assertEquals(test.getLegalEntityId(), LEGAL_ENTITY);
assertEquals(test.getCurrency(), EUR);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getAccrualSchedule(), PERIOD_SCHEDULE);
assertEquals(test.getSettlementDateOffset(), DATE_OFFSET);
assertEquals(test.getYieldConvention(), YIELD_CONVENTION);
assertEquals(test.getExCouponPeriod(), EX_COUPON);
}
public void test_builder_fail() {
assertThrowsIllegalArg(() -> FixedCouponBond.builder()
.securityId(SECURITY_ID)
.dayCount(DAY_COUNT)
.fixedRate(FIXED_RATE)
.legalEntityId(LEGAL_ENTITY)
.currency(EUR)
.notional(NOTIONAL)
.accrualSchedule(PERIOD_SCHEDULE)
.settlementDateOffset(DATE_OFFSET)
.yieldConvention(YIELD_CONVENTION)
.exCouponPeriod(DaysAdjustment.ofBusinessDays(EX_COUPON_DAYS, EUTA, BUSINESS_ADJUST))
.build());
assertThrowsIllegalArg(() -> FixedCouponBond.builder()
.securityId(SECURITY_ID)
.dayCount(DAY_COUNT)
.fixedRate(FIXED_RATE)
.legalEntityId(LEGAL_ENTITY)
.currency(EUR)
.notional(NOTIONAL)
.accrualSchedule(PERIOD_SCHEDULE)
.settlementDateOffset(DaysAdjustment.ofBusinessDays(-3, EUTA))
.yieldConvention(YIELD_CONVENTION)
.build());
}
public void test_resolve() {
FixedCouponBond base = sut();
ResolvedFixedCouponBond resolved = base.resolve(REF_DATA);
assertEquals(resolved.getLegalEntityId(), LEGAL_ENTITY);
assertEquals(resolved.getSettlementDateOffset(), DATE_OFFSET);
assertEquals(resolved.getYieldConvention(), YIELD_CONVENTION);
ImmutableList<FixedCouponBondPaymentPeriod> periodicPayments = resolved.getPeriodicPayments();
int expNum = 20;
assertEquals(periodicPayments.size(), expNum);
LocalDate unadjustedEnd = END_DATE;
Schedule unadjusted = PERIOD_SCHEDULE.createSchedule(REF_DATA).toUnadjusted();
for (int i = 0; i < expNum; ++i) {
FixedCouponBondPaymentPeriod payment = periodicPayments.get(expNum - 1 - i);
assertEquals(payment.getCurrency(), EUR);
assertEquals(payment.getNotional(), NOTIONAL);
assertEquals(payment.getFixedRate(), FIXED_RATE);
assertEquals(payment.getUnadjustedEndDate(), unadjustedEnd);
assertEquals(payment.getEndDate(), BUSINESS_ADJUST.adjust(unadjustedEnd, REF_DATA));
assertEquals(payment.getPaymentDate(), payment.getEndDate());
LocalDate unadjustedStart = unadjustedEnd.minusMonths(6);
assertEquals(payment.getUnadjustedStartDate(), unadjustedStart);
assertEquals(payment.getStartDate(), BUSINESS_ADJUST.adjust(unadjustedStart, REF_DATA));
assertEquals(payment.getYearFraction(), unadjusted.getPeriod(expNum - 1 - i).yearFraction(DAY_COUNT, unadjusted));
assertEquals(payment.getDetachmentDate(), EX_COUPON.adjust(payment.getPaymentDate(), REF_DATA));
unadjustedEnd = unadjustedStart;
}
Payment expectedPayment = Payment.of(CurrencyAmount.of(EUR, NOTIONAL), BUSINESS_ADJUST.adjust(END_DATE, REF_DATA));
assertEquals(resolved.getNominalPayment(), expectedPayment);
}
//-------------------------------------------------------------------------
public void coverage() {
coverImmutableBean(sut());
coverBeanEquals(sut(), sut2());
}
public void test_serialization() {
assertSerialization(sut());
}
//-------------------------------------------------------------------------
static FixedCouponBond sut() {
return FixedCouponBond.builder()
.securityId(SECURITY_ID)
.dayCount(DAY_COUNT)
.fixedRate(FIXED_RATE)
.legalEntityId(LEGAL_ENTITY)
.currency(EUR)
.notional(NOTIONAL)
.accrualSchedule(PERIOD_SCHEDULE)
.settlementDateOffset(DATE_OFFSET)
.yieldConvention(YIELD_CONVENTION)
.exCouponPeriod(EX_COUPON)
.build();
}
static FixedCouponBond sut2() {
BusinessDayAdjustment adj = BusinessDayAdjustment.of(MODIFIED_FOLLOWING, SAT_SUN);
PeriodicSchedule sche = PeriodicSchedule.of(
START_DATE, END_DATE, Frequency.P12M, adj, StubConvention.SHORT_INITIAL, true);
return FixedCouponBond.builder()
.securityId(SECURITY_ID2)
.dayCount(DayCounts.ACT_360)
.fixedRate(0.005)
.legalEntityId(StandardId.of("OG-Ticker", "BUN EUR 2"))
.currency(GBP)
.notional(1.0e6)
.accrualSchedule(sche)
.settlementDateOffset(DaysAdjustment.ofBusinessDays(2, SAT_SUN))
.yieldConvention(FixedCouponBondYieldConvention.GB_BUMP_DMO)
.build();
}
}