/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.dsf;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.basics.date.BusinessDayConventions.MODIFIED_FOLLOWING;
import static com.opengamma.strata.basics.date.BusinessDayConventions.PRECEDING;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static com.opengamma.strata.basics.date.DayCounts.THIRTY_U_360;
import static com.opengamma.strata.basics.index.IborIndices.USD_LIBOR_3M;
import static com.opengamma.strata.basics.schedule.Frequency.P3M;
import static com.opengamma.strata.basics.schedule.Frequency.P6M;
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 static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import org.testng.annotations.Test;
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.MultiCurrencyAmount;
import com.opengamma.strata.basics.date.BusinessDayAdjustment;
import com.opengamma.strata.basics.date.DaysAdjustment;
import com.opengamma.strata.basics.date.HolidayCalendarId;
import com.opengamma.strata.basics.date.HolidayCalendarIds;
import com.opengamma.strata.basics.schedule.PeriodicSchedule;
import com.opengamma.strata.basics.schedule.StubConvention;
import com.opengamma.strata.basics.value.ValueSchedule;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.market.curve.CurveMetadata;
import com.opengamma.strata.market.curve.CurveName;
import com.opengamma.strata.market.curve.Curves;
import com.opengamma.strata.market.curve.InterpolatedNodalCurve;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolators;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator;
import com.opengamma.strata.product.SecurityId;
import com.opengamma.strata.product.TradeInfo;
import com.opengamma.strata.product.dsf.Dsf;
import com.opengamma.strata.product.dsf.ResolvedDsf;
import com.opengamma.strata.product.dsf.ResolvedDsfTrade;
import com.opengamma.strata.product.swap.FixedRateCalculation;
import com.opengamma.strata.product.swap.IborRateCalculation;
import com.opengamma.strata.product.swap.NotionalSchedule;
import com.opengamma.strata.product.swap.PaymentSchedule;
import com.opengamma.strata.product.swap.RateCalculationSwapLeg;
import com.opengamma.strata.product.swap.Swap;
import com.opengamma.strata.product.swap.SwapLeg;
/**
* Test {@link DiscountingDsfTradePricer}.
*/
@Test
public class DiscountingDsfTradePricerTest {
private static final ReferenceData REF_DATA = ReferenceData.standard();
// curves
private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR;
private static final LocalDate VAL_DATE = LocalDate.of(2013, 3, 28);
private static final DoubleArray USD_DSC_TIME = DoubleArray.of(0.0, 0.5, 1.0, 2.0, 5.0, 10.0);
private static final DoubleArray USD_DSC_RATE = DoubleArray.of(0.0100, 0.0120, 0.0120, 0.0140, 0.0140, 0.0140);
private static final CurveName USD_DSC_NAME = CurveName.of("USD Dsc");
private static final CurveMetadata USD_DSC_METADATA = Curves.zeroRates(USD_DSC_NAME, ACT_ACT_ISDA);
private static final InterpolatedNodalCurve USD_DSC =
InterpolatedNodalCurve.of(USD_DSC_METADATA, USD_DSC_TIME, USD_DSC_RATE, INTERPOLATOR);
private static final DoubleArray USD_FWD3_TIME = DoubleArray.of(0.0, 0.5, 1.0, 2.0, 5.0, 10.0);
private static final DoubleArray USD_FWD3_RATE = DoubleArray.of(0.0150, 0.0125, 0.0150, 0.0175, 0.0150, 0.0150);
private static final CurveName USD_FWD3_NAME = CurveName.of("USD LIBOR 3M");
private static final CurveMetadata USD_FWD3_METADATA = Curves.zeroRates(USD_FWD3_NAME, ACT_ACT_ISDA);
private static final InterpolatedNodalCurve USD_FWD3 =
InterpolatedNodalCurve.of(USD_FWD3_METADATA, USD_FWD3_TIME, USD_FWD3_RATE, INTERPOLATOR);
private static final ImmutableRatesProvider PROVIDER = ImmutableRatesProvider.builder(VAL_DATE)
.discountCurve(USD, USD_DSC)
.iborIndexCurve(USD_LIBOR_3M, USD_FWD3)
.build();
// underlying swap
private static final NotionalSchedule UNIT_NOTIONAL = NotionalSchedule.of(USD, 1d);
private static final HolidayCalendarId CALENDAR = HolidayCalendarIds.SAT_SUN;
private static final BusinessDayAdjustment BDA_MF = BusinessDayAdjustment.of(MODIFIED_FOLLOWING, CALENDAR);
private static final BusinessDayAdjustment BDA_P = BusinessDayAdjustment.of(PRECEDING, CALENDAR);
private static final LocalDate START = LocalDate.of(2013, 6, 19);
private static final LocalDate END = START.plusYears(10);
private static final double RATE = 0.0175;
private static final SwapLeg FIXED_LEG = RateCalculationSwapLeg.builder()
.payReceive(RECEIVE)
.accrualSchedule(PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(P6M)
.businessDayAdjustment(BDA_MF)
.stubConvention(StubConvention.SHORT_FINAL)
.build())
.paymentSchedule(PaymentSchedule.builder()
.paymentFrequency(P6M)
.paymentDateOffset(DaysAdjustment.NONE)
.build())
.notionalSchedule(UNIT_NOTIONAL)
.calculation(FixedRateCalculation.builder()
.dayCount(THIRTY_U_360)
.rate(ValueSchedule.of(RATE))
.build())
.build();
private static final SwapLeg IBOR_LEG = RateCalculationSwapLeg.builder()
.payReceive(PAY)
.accrualSchedule(PeriodicSchedule.builder()
.startDate(START)
.endDate(END)
.frequency(P3M)
.businessDayAdjustment(BDA_MF)
.stubConvention(StubConvention.SHORT_FINAL)
.build())
.paymentSchedule(PaymentSchedule.builder()
.paymentFrequency(P3M)
.paymentDateOffset(DaysAdjustment.NONE)
.build())
.notionalSchedule(UNIT_NOTIONAL)
.calculation(IborRateCalculation.builder()
.index(USD_LIBOR_3M)
.fixingDateOffset(DaysAdjustment.ofBusinessDays(-2, CALENDAR, BDA_P))
.build())
.build();
private static final Swap SWAP = Swap.of(FIXED_LEG, IBOR_LEG);
// deliverable swap future
private static final LocalDate LAST_TRADE = LocalDate.of(2013, 6, 17);
private static final LocalDate DELIVERY = LocalDate.of(2013, 6, 19);
private static final double NOTIONAL = 100000;
private static final StandardId DSF_ID = StandardId.of("OG-Ticker", "DSF1");
private static final ResolvedDsf FUTURE = Dsf.builder()
.securityId(SecurityId.of(DSF_ID))
.deliveryDate(DELIVERY)
.lastTradeDate(LAST_TRADE)
.notional(NOTIONAL)
.underlyingSwap(SWAP)
.build()
.resolve(REF_DATA);
private static final TradeInfo TRADE_INFO = TradeInfo.builder().tradeDate(VAL_DATE).build();
private static final double TRADE_PRICE = 0.98 + 31.0 / 32.0 / 100.0; // price quoted in 32nd of 1%
private static final long QUANTITY = 1234L;
private static final ResolvedDsfTrade FUTURE_TRADE = ResolvedDsfTrade.builder()
.info(TRADE_INFO)
.product(FUTURE)
.quantity(QUANTITY)
.price(TRADE_PRICE)
.build();
private static final double LASTMARG_PRICE = 0.99 + 8.0 / 32.0 / 100.0;
// calculators
private static final double TOL = 1.0e-13;
private static final double EPS = 1.0e-6;
private static final DiscountingDsfProductPricer PRODUCT_PRICER =
DiscountingDsfProductPricer.DEFAULT;
private static final DiscountingDsfTradePricer TRADE_PRICER =
DiscountingDsfTradePricer.DEFAULT;
private static final RatesFiniteDifferenceSensitivityCalculator FD_CAL =
new RatesFiniteDifferenceSensitivityCalculator(EPS);
public void test_price() {
double computed = TRADE_PRICER.price(FUTURE_TRADE, PROVIDER);
double expected = PRODUCT_PRICER.price(FUTURE, PROVIDER);
assertEquals(computed, expected, TOL);
}
public void test_presentValue() {
CurrencyAmount computed = TRADE_PRICER.presentValue(FUTURE_TRADE, PROVIDER, LASTMARG_PRICE);
double expected = QUANTITY * NOTIONAL * (PRODUCT_PRICER.price(FUTURE, PROVIDER) - TRADE_PRICE);
assertEquals(computed.getCurrency(), USD);
assertEquals(computed.getAmount(), expected, QUANTITY * NOTIONAL * TOL);
}
public void test_presentValueSensitivity() {
PointSensitivities point = TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE, PROVIDER);
CurrencyParameterSensitivities computed = PROVIDER.parameterSensitivity(point);
CurrencyParameterSensitivities expected = FD_CAL.sensitivity(
PROVIDER, (p) -> TRADE_PRICER.presentValue(FUTURE_TRADE, (p), LASTMARG_PRICE));
assertTrue(computed.equalWithTolerance(expected, NOTIONAL * QUANTITY * EPS * 10d));
}
public void test_currencyExposure() {
CurrencyAmount pv = TRADE_PRICER.presentValue(FUTURE_TRADE, PROVIDER, LASTMARG_PRICE);
PointSensitivities point = TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE, PROVIDER);
MultiCurrencyAmount expected = PROVIDER.currencyExposure(point).plus(pv);
MultiCurrencyAmount computed = TRADE_PRICER.currencyExposure(FUTURE_TRADE, PROVIDER, LASTMARG_PRICE);
assertEquals(computed, expected);
}
//-------------------------------------------------------------------------
// regression to 2.x
public void regression() {
CurrencyAmount pv = TRADE_PRICER.presentValue(FUTURE_TRADE, PROVIDER, TRADE_PRICE);
assertEquals(pv.getAmount(), 4022633.290539182, NOTIONAL * QUANTITY * TOL);
DoubleArray dscExp = DoubleArray.of(
347963.1427498563, 240275.26230191416, 123908.37739051704,
-1302968.1341957184, -8402797.591029292, -9024590.733895564);
DoubleArray fwdExp =DoubleArray.of(
1.5288758221797276E7, 1.2510651813905597E7, -1535786.53682933,
-9496881.09854053, -3.583343769759877E7, -1.1342379328462188E9);
PointSensitivities point = TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE, PROVIDER);
CurrencyParameterSensitivities sensi = PROVIDER.parameterSensitivity(point);
double tolerance = NOTIONAL * QUANTITY * EPS;
assertTrue(sensi.getSensitivity(USD_DSC_NAME, USD).getSensitivity().equalWithTolerance(dscExp, tolerance));
assertTrue(sensi.getSensitivity(USD_FWD3_NAME, USD).getSensitivity().equalWithTolerance(fwdExp, tolerance));
}
}