/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.product.capfloor;
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.HolidayCalendarIds.EUTA;
import static com.opengamma.strata.basics.index.IborIndices.EUR_EURIBOR_3M;
import static com.opengamma.strata.basics.index.IborIndices.GBP_LIBOR_6M;
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.common.PayReceive.PAY;
import static com.opengamma.strata.product.common.PayReceive.RECEIVE;
import static org.testng.Assert.assertEquals;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.date.AdjustableDate;
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.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.rate.IborRateComputation;
import com.opengamma.strata.product.swap.FixingRelativeTo;
import com.opengamma.strata.product.swap.IborRateCalculation;
/**
* Test {@link IborCapFloorLeg}.
*/
@Test
public class IborCapFloorLegTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
private static final LocalDate START = LocalDate.of(2011, 3, 17);
private static final LocalDate END = LocalDate.of(2012, 3, 17);
private static final IborRateCalculation RATE_CALCULATION = IborRateCalculation.of(EUR_EURIBOR_3M);
private static final Frequency FREQUENCY = Frequency.P3M;
private static final BusinessDayAdjustment BUSS_ADJ =
BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, EUTA);
private static final PeriodicSchedule SCHEDULE = PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(FREQUENCY)
.businessDayAdjustment(BUSS_ADJ)
.stubConvention(StubConvention.NONE)
.build();
private static final DaysAdjustment PAYMENT_OFFSET = DaysAdjustment.ofBusinessDays(2, EUTA);
private static final double[] NOTIONALS = new double[] {1.0e6, 1.2e6, 0.8e6, 1.0e6};
private static final double[] STRIKES = new double[] {0.03, 0.0275, 0.02, 0.0345};
private static final ValueSchedule CAP = ValueSchedule.of(0.0325);
private static final List<ValueStep> FLOOR_STEPS = new ArrayList<ValueStep>();
private static final List<ValueStep> NOTIONAL_STEPS = new ArrayList<ValueStep>();
static {
FLOOR_STEPS.add(ValueStep.of(1, ValueAdjustment.ofReplace(STRIKES[1])));
FLOOR_STEPS.add(ValueStep.of(2, ValueAdjustment.ofReplace(STRIKES[2])));
FLOOR_STEPS.add(ValueStep.of(3, ValueAdjustment.ofReplace(STRIKES[3])));
NOTIONAL_STEPS.add(ValueStep.of(1, ValueAdjustment.ofReplace(NOTIONALS[1])));
NOTIONAL_STEPS.add(ValueStep.of(2, ValueAdjustment.ofReplace(NOTIONALS[2])));
NOTIONAL_STEPS.add(ValueStep.of(3, ValueAdjustment.ofReplace(NOTIONALS[3])));
}
private static final ValueSchedule FLOOR = ValueSchedule.of(STRIKES[0], FLOOR_STEPS);
private static final ValueSchedule NOTIONAL = ValueSchedule.of(NOTIONALS[0], NOTIONAL_STEPS);
public void test_builder_full() {
IborCapFloorLeg test = IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.capSchedule(CAP)
.currency(GBP)
.notional(NOTIONAL)
.paymentDateOffset(PAYMENT_OFFSET)
.paymentSchedule(SCHEDULE)
.payReceive(PAY)
.build();
assertEquals(test.getCalculation(), RATE_CALCULATION);
assertEquals(test.getCapSchedule().get(), CAP);
assertEquals(test.getFloorSchedule().isPresent(), false);
assertEquals(test.getCurrency(), GBP);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getPaymentDateOffset(), PAYMENT_OFFSET);
assertEquals(test.getPaymentSchedule(), SCHEDULE);
assertEquals(test.getPayReceive(), PAY);
assertEquals(test.getStartDate(), AdjustableDate.of(START, BUSS_ADJ));
assertEquals(test.getEndDate(), AdjustableDate.of(END, BUSS_ADJ));
assertEquals(test.getIndex(), EUR_EURIBOR_3M);
}
public void test_builder_min() {
IborCapFloorLeg test = IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.floorSchedule(FLOOR)
.notional(NOTIONAL)
.paymentSchedule(SCHEDULE)
.payReceive(RECEIVE)
.build();
assertEquals(test.getCalculation(), RATE_CALCULATION);
assertEquals(test.getCapSchedule().isPresent(), false);
assertEquals(test.getFloorSchedule().get(), FLOOR);
assertEquals(test.getCurrency(), EUR);
assertEquals(test.getNotional(), NOTIONAL);
assertEquals(test.getPaymentDateOffset(), DaysAdjustment.NONE);
assertEquals(test.getPaymentSchedule(), SCHEDULE);
assertEquals(test.getPayReceive(), RECEIVE);
assertEquals(test.getStartDate(), AdjustableDate.of(START, BUSS_ADJ));
assertEquals(test.getEndDate(), AdjustableDate.of(END, BUSS_ADJ));
}
public void test_builder_fail() {
// cap and floor present
assertThrowsIllegalArg(() -> IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.capSchedule(CAP)
.floorSchedule(FLOOR)
.notional(NOTIONAL)
.paymentSchedule(SCHEDULE)
.payReceive(RECEIVE)
.build());
// cap and floor missing
assertThrowsIllegalArg(() -> IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.notional(NOTIONAL)
.paymentSchedule(PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(FREQUENCY)
.businessDayAdjustment(BUSS_ADJ)
.build())
.payReceive(RECEIVE)
.build());
// stub type
assertThrowsIllegalArg(() -> IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.capSchedule(CAP)
.currency(GBP)
.notional(NOTIONAL)
.paymentDateOffset(PAYMENT_OFFSET)
.paymentSchedule(PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(FREQUENCY)
.businessDayAdjustment(BUSS_ADJ)
.stubConvention(StubConvention.SHORT_FINAL)
.build())
.payReceive(PAY)
.build());
}
public void test_resolve_cap() {
IborRateCalculation rateCalc = IborRateCalculation.builder()
.index(EUR_EURIBOR_3M)
.fixingRelativeTo(FixingRelativeTo.PERIOD_END)
.fixingDateOffset(EUR_EURIBOR_3M.getFixingDateOffset())
.build();
IborCapFloorLeg base = IborCapFloorLeg.builder()
.calculation(rateCalc)
.capSchedule(CAP)
.notional(NOTIONAL)
.paymentDateOffset(PAYMENT_OFFSET)
.paymentSchedule(SCHEDULE)
.payReceive(RECEIVE)
.build();
LocalDate[] unadjustedDates =
new LocalDate[] {START, START.plusMonths(3), START.plusMonths(6), START.plusMonths(9), START.plusMonths(12)};
IborCapletFloorletPeriod[] periods = new IborCapletFloorletPeriod[4];
for (int i = 0; i < 4; ++i) {
LocalDate start = BUSS_ADJ.adjust(unadjustedDates[i], REF_DATA);
LocalDate end = BUSS_ADJ.adjust(unadjustedDates[i + 1], REF_DATA);
double yearFraction = EUR_EURIBOR_3M.getDayCount().relativeYearFraction(start, end);
periods[i] = IborCapletFloorletPeriod.builder()
.caplet(CAP.getInitialValue())
.currency(EUR)
.startDate(start)
.endDate(end)
.unadjustedStartDate(unadjustedDates[i])
.unadjustedEndDate(unadjustedDates[i + 1])
.paymentDate(PAYMENT_OFFSET.adjust(end, REF_DATA))
.notional(NOTIONALS[i])
.iborRate(IborRateComputation.of(EUR_EURIBOR_3M, rateCalc.getFixingDateOffset().adjust(end, REF_DATA), REF_DATA))
.yearFraction(yearFraction)
.build();
}
ResolvedIborCapFloorLeg expected = ResolvedIborCapFloorLeg.builder()
.capletFloorletPeriods(periods)
.payReceive(RECEIVE)
.build();
ResolvedIborCapFloorLeg computed = base.resolve(REF_DATA);
assertEquals(computed, expected);
}
public void test_resolve_floor() {
IborCapFloorLeg base = IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.floorSchedule(FLOOR)
.currency(GBP)
.notional(NOTIONAL)
.paymentDateOffset(PAYMENT_OFFSET)
.paymentSchedule(SCHEDULE)
.payReceive(PAY)
.build();
LocalDate[] unadjustedDates =
new LocalDate[] {START, START.plusMonths(3), START.plusMonths(6), START.plusMonths(9), START.plusMonths(12)};
IborCapletFloorletPeriod[] periods = new IborCapletFloorletPeriod[4];
for (int i = 0; i < 4; ++i) {
LocalDate start = BUSS_ADJ.adjust(unadjustedDates[i], REF_DATA);
LocalDate end = BUSS_ADJ.adjust(unadjustedDates[i + 1], REF_DATA);
double yearFraction = EUR_EURIBOR_3M.getDayCount().relativeYearFraction(start, end);
LocalDate fixingDate = RATE_CALCULATION.getFixingDateOffset().adjust(start, REF_DATA);
periods[i] = IborCapletFloorletPeriod.builder()
.floorlet(STRIKES[i])
.currency(GBP)
.startDate(start)
.endDate(end)
.unadjustedStartDate(unadjustedDates[i])
.unadjustedEndDate(unadjustedDates[i + 1])
.paymentDate(PAYMENT_OFFSET.adjust(end, REF_DATA))
.notional(-NOTIONALS[i])
.iborRate(IborRateComputation.of(EUR_EURIBOR_3M, fixingDate, REF_DATA))
.yearFraction(yearFraction)
.build();
}
ResolvedIborCapFloorLeg expected = ResolvedIborCapFloorLeg.builder()
.capletFloorletPeriods(periods)
.payReceive(PAY)
.build();
ResolvedIborCapFloorLeg computed = base.resolve(REF_DATA);
assertEquals(computed, expected);
}
//-------------------------------------------------------------------------
public void coverage() {
IborCapFloorLeg test1 = IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.floorSchedule(FLOOR)
.notional(NOTIONAL)
.paymentSchedule(SCHEDULE)
.payReceive(RECEIVE)
.build();
coverImmutableBean(test1);
IborCapFloorLeg test2 = IborCapFloorLeg.builder()
.calculation(IborRateCalculation.of(GBP_LIBOR_6M))
.capSchedule(CAP)
.notional(ValueSchedule.of(1000))
.paymentDateOffset(PAYMENT_OFFSET)
.paymentSchedule(PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(Frequency.P6M)
.businessDayAdjustment(BUSS_ADJ)
.build())
.payReceive(PAY)
.build();
coverBeanEquals(test1, test2);
}
public void test_serialization() {
IborCapFloorLeg test = IborCapFloorLeg.builder()
.calculation(RATE_CALCULATION)
.floorSchedule(FLOOR)
.notional(NOTIONAL)
.paymentSchedule(SCHEDULE)
.payReceive(RECEIVE)
.build();
assertSerialization(test);
}
}