
package com.opengamma.strata.pricer.bond.e2e;

import static com.opengamma.strata.basics.currency.Currency.JPY;
import static com.opengamma.strata.basics.date.BusinessDayConventions.FOLLOWING;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

import java.time.LocalDate;

import org.testng.annotations.Test;

import com.google.common.collect.ImmutableMap;
import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.StandardId;
import com.opengamma.strata.basics.currency.Currency;
import com.opengamma.strata.basics.currency.CurrencyAmount;
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.date.HolidayCalendarId;
import com.opengamma.strata.basics.date.HolidayCalendarIds;
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.collect.DoubleArrayMath;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.tuple.Pair;
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.DiscountFactors;
import com.opengamma.strata.pricer.ZeroRateDiscountFactors;
import com.opengamma.strata.pricer.bond.RepoGroup;
import com.opengamma.strata.pricer.bond.DiscountingBondFutureProductPricer;
import com.opengamma.strata.pricer.bond.DiscountingBondFutureTradePricer;
import com.opengamma.strata.pricer.bond.ImmutableLegalEntityDiscountingProvider;
import com.opengamma.strata.pricer.bond.LegalEntityDiscountingProvider;
import com.opengamma.strata.pricer.bond.LegalEntityGroup;
import com.opengamma.strata.product.SecurityId;
import com.opengamma.strata.product.TradeInfo;
import com.opengamma.strata.product.bond.BondFuture;
import com.opengamma.strata.product.bond.FixedCouponBond;
import com.opengamma.strata.product.bond.FixedCouponBondYieldConvention;
import com.opengamma.strata.product.bond.ResolvedBondFuture;
import com.opengamma.strata.product.bond.ResolvedBondFutureTrade;

/**
 * End to end test on JPY-dominated trades.
 * <p>
 * The trades involve futures contract on 10yr bonds.
 */
@SuppressWarnings("unchecked")
@Test
public class BondFuturesJpyEnd2EndTest {

  private static final ReferenceData REF_DATA = ReferenceData.standard();
  private static final double ONE_PERCENT = 1.0E-2;
  private static final double ONE_BASIS_POINT = 1.0E-4;
  private static final double HUNDRED = 100d;
  private static final double TOL = 1.0E-14;
  private static final LocalDate VALUATION = LocalDate.of(2016, 2, 10);
  private static final TradeInfo TRADE_INFO = TradeInfo.builder().tradeDate(VALUATION.minusDays(1)).build();
  private static final double NOTIONAL = 100_000_000D;
  private static final long QUANTITY = 1L;
  // Fixed coupon bonds
  private static final StandardId ISSUER_ID = StandardId.of("OG-Ticker", "GOVT");
  private static final LegalEntityGroup GROUP_ISSUER = LegalEntityGroup.of("GOVT");
  private static final RepoGroup GROUP_REPO = RepoGroup.of("GOVT BONDS");
  private static final FixedCouponBondYieldConvention YIELD_CONVENTION = FixedCouponBondYieldConvention.JP_SIMPLE;
  /** Notional of underlying bond */
  private static final HolidayCalendarId CALENDAR = HolidayCalendarIds.JPTO;
  private static final DaysAdjustment SETTLEMENT_DAYS = DaysAdjustment.ofBusinessDays(3, CALENDAR);
  private static final DayCount DAY_COUNT = DayCounts.NL_365;
  private static final BusinessDayAdjustment BUSINESS_ADJUST = BusinessDayAdjustment.of(FOLLOWING, CALENDAR);
  private static final int NB_UND_BONDS = 14;
  private static final double[] UND_RATES =
      new double[] {0.6, 0.8, 0.8, 0.6, 0.6, 0.6, 0.6, 0.5, 0.5, 0.3, 0.4, 0.4, 0.4, 0.3 };
  private static final LocalDate[] START_DATE = new LocalDate[] {
    LocalDate.of(2015, 3, 20), LocalDate.of(2015, 6, 20), LocalDate.of(2015, 9, 20), LocalDate.of(2015, 9, 20), 
    LocalDate.of(2015, 12, 20), LocalDate.of(2015, 3, 20), LocalDate.of(2015, 6, 20), LocalDate.of(2015, 9, 20), 
    LocalDate.of(2015, 12, 20), LocalDate.of(2015, 12, 20), LocalDate.of(2015, 3, 20), LocalDate.of(2015, 6, 20), 
    LocalDate.of(2015, 9, 20), LocalDate.of(2015, 12, 20) };
  private static final LocalDate[] MATURITY_DATE = new LocalDate[] {
    LocalDate.of(2023, 3, 20), LocalDate.of(2023, 6, 20), LocalDate.of(2023, 9, 20), LocalDate.of(2023, 9, 20), 
    LocalDate.of(2023, 12, 20), LocalDate.of(2024, 3, 20), LocalDate.of(2024, 6, 20), LocalDate.of(2024, 9, 20), 
    LocalDate.of(2024, 12, 20), LocalDate.of(2024, 12, 20), LocalDate.of(2025, 3, 20), LocalDate.of(2025, 6, 20), 
    LocalDate.of(2025, 9, 20), LocalDate.of(2025, 12, 20) };
  private static final StandardId[] BOND_SECURITY_ID = new StandardId[] {StandardId.of("OG-Ticker", "GOVT-BOND0"),
    StandardId.of("OG-Ticker", "GOVT-BOND1"), StandardId.of("OG-Ticker", "GOVT-BOND2"),
    StandardId.of("OG-Ticker", "GOVT-BOND3"), StandardId.of("OG-Ticker", "GOVT-BOND4"),
    StandardId.of("OG-Ticker", "GOVT-BOND5"), StandardId.of("OG-Ticker", "GOVT-BOND6"),
    StandardId.of("OG-Ticker", "GOVT-BOND7"), StandardId.of("OG-Ticker", "GOVT-BOND8"),
    StandardId.of("OG-Ticker", "GOVT-BOND9"), StandardId.of("OG-Ticker", "GOVT-BOND10"),
    StandardId.of("OG-Ticker", "GOVT-BOND11"), StandardId.of("OG-Ticker", "GOVT-BOND12"),
    StandardId.of("OG-Ticker", "GOVT-BOND13") };
  private static final FixedCouponBond[] UND_BOND = new FixedCouponBond[NB_UND_BONDS];
  static {
    for (int i = 0; i < NB_UND_BONDS; ++i) {
      PeriodicSchedule periodSchedule = PeriodicSchedule.of(
          START_DATE[i], MATURITY_DATE[i], Frequency.P6M, BUSINESS_ADJUST, StubConvention.SHORT_INITIAL, false);
      FixedCouponBond product = FixedCouponBond.builder()
          .securityId(SecurityId.of(BOND_SECURITY_ID[i]))
          .dayCount(DAY_COUNT)
          .fixedRate(UND_RATES[i] * ONE_PERCENT)
          .legalEntityId(ISSUER_ID)
          .currency(JPY)
          .notional(NOTIONAL)
          .accrualSchedule(periodSchedule)
          .settlementDateOffset(SETTLEMENT_DAYS)
          .yieldConvention(YIELD_CONVENTION)
          .build();
      UND_BOND[i] = product;
    }
  }
  private static final DaysAdjustment LAST_TRADE_ADJUST = DaysAdjustment.ofBusinessDays(-5, CALENDAR);
  // Futures in September 2016
  private static final FixedCouponBond[] UND_BOND_SEP;
  static {
    UND_BOND_SEP = new FixedCouponBond[NB_UND_BONDS - 2];
    System.arraycopy(UND_BOND, 2, UND_BOND_SEP, 0, NB_UND_BONDS - 2);
  }
  private static final Double[] CF_SEP = new Double[] {0.706302, 0.695006, 0.686265, 0.677675, 0.669189, 0.654569,
    0.646180, 0.633317, 0.631348, 0.623057, 0.614901, 0.599832 };
  private static final LocalDate EFFECTIVE_DATE_SEP = LocalDate.of(2016, 9, 20);
  private static final LocalDate FIRST_DELIVERY_DATE_SEP = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_SEP, REF_DATA);
  private static final LocalDate LAST_DELIVERY_DATE_SEP = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_SEP, REF_DATA);
  private static final LocalDate LAST_TRADING_DATE_SEP = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_SEP, REF_DATA);
  private static final LocalDate FIRST_NOTICE_DATE_SEP = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_SEP, REF_DATA);
  private static final LocalDate LAST_NOTICE_DATE_SEP = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_SEP, REF_DATA);
  private static final SecurityId FUTURE_SECURITY_ID_SEP = SecurityId.of("OG-Ticker", "GOVT-BOND-FUT-SEP");
  private static final ResolvedBondFuture FUTURE_PRODUCT_SEP = BondFuture.builder()
      .securityId(FUTURE_SECURITY_ID_SEP)
      .deliveryBasket(UND_BOND_SEP)
      .conversionFactors(CF_SEP)
      .firstNoticeDate(FIRST_NOTICE_DATE_SEP)
      .lastNoticeDate(LAST_NOTICE_DATE_SEP)
      .firstDeliveryDate(FIRST_DELIVERY_DATE_SEP)
      .lastDeliveryDate(LAST_DELIVERY_DATE_SEP)
      .lastTradeDate(LAST_TRADING_DATE_SEP)
      .rounding(Rounding.ofDecimalPlaces(2))
      .build()
      .resolve(REF_DATA);
  private static final ResolvedBondFutureTrade FUTURE_TRADE_SEP = ResolvedBondFutureTrade.builder()
      .quantity(QUANTITY)
      .product(FUTURE_PRODUCT_SEP)
      .info(TRADE_INFO)
      .build();
  private static final double REF_PRICE_SEP = 151.61;
  // Futures in June 2016
  private static final Double[] CF_JUN = new Double[] {0.706302, 0.697881, 0.686265, 0.677675, 0.669189, 0.660850,
    0.646180, 0.637931, 0.624765, 0.623057, 0.614901, 0.606851, 0.591771 };
  private static final FixedCouponBond[] UND_BOND_JUN;
  static {
    UND_BOND_JUN = new FixedCouponBond[NB_UND_BONDS - 1];
    System.arraycopy(UND_BOND, 1, UND_BOND_JUN, 0, NB_UND_BONDS - 1);
  }
  private static final LocalDate EFFECTIVE_DATE_JUN = LocalDate.of(2016, 6, 20);
  private static final LocalDate FIRST_DELIVERY_DATE_JUN = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_JUN, REF_DATA);
  private static final LocalDate LAST_DELIVERY_DATE_JUN = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_JUN, REF_DATA);
  private static final LocalDate LAST_TRADING_DATE_JUN = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_JUN, REF_DATA);
  private static final LocalDate FIRST_NOTICE_DATE_JUN = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_JUN, REF_DATA);
  private static final LocalDate LAST_NOTICE_DATE_JUN = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_JUN, REF_DATA);
  private static final SecurityId FUTURE_SECURITY_ID_JUN = SecurityId.of("OG-Ticker", "GOVT-BOND-FUT-JUN");
  private static final ResolvedBondFuture FUTURE_PRODUCT_JUN = BondFuture.builder()
      .securityId(FUTURE_SECURITY_ID_JUN)
      .deliveryBasket(UND_BOND_JUN)
      .conversionFactors(CF_JUN)
      .firstNoticeDate(FIRST_NOTICE_DATE_JUN)
      .lastNoticeDate(LAST_NOTICE_DATE_JUN)
      .firstDeliveryDate(FIRST_DELIVERY_DATE_JUN)
      .lastDeliveryDate(LAST_DELIVERY_DATE_JUN)
      .lastTradeDate(LAST_TRADING_DATE_JUN)
      .rounding(Rounding.ofDecimalPlaces(2))
      .build()
      .resolve(REF_DATA);
  private static final ResolvedBondFutureTrade FUTURE_TRADE_JUN = ResolvedBondFutureTrade.builder()
      .product(FUTURE_PRODUCT_JUN)
      .info(TRADE_INFO)
      .quantity(QUANTITY)
      .price(123)
      .build();
  private static final double REF_PRICE_JUN = 151.73;
  // Futures in March 2016
  private static final Double[] CF_MAR = new Double[] {0.695006, 0.697881, 0.689613, 0.677675, 0.669189,
    0.660850, 0.652611, 0.637931, 0.629786, 0.616327, 0.614901, 0.606851, 0.598933, 0.583818 };
  private static final LocalDate EFFECTIVE_DATE_MAR = LocalDate.of(2016, 3, 20);
  private static final LocalDate FIRST_DELIVERY_DATE_MAR = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_MAR, REF_DATA);
  private static final LocalDate LAST_DELIVERY_DATE_MAR = BUSINESS_ADJUST.adjust(EFFECTIVE_DATE_MAR, REF_DATA);
  private static final LocalDate LAST_TRADING_DATE_MAR = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_MAR, REF_DATA);
  private static final LocalDate FIRST_NOTICE_DATE_MAR = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_MAR, REF_DATA);
  private static final LocalDate LAST_NOTICE_DATE_MAR = LAST_TRADE_ADJUST.adjust(LAST_DELIVERY_DATE_MAR, REF_DATA);
  private static final SecurityId FUTURE_SECURITY_ID_MAR = SecurityId.of("OG-Ticker", "GOVT-BOND-FUT-MAR");
  private static final ResolvedBondFuture FUTURE_PRODUCT_MAR = BondFuture.builder()
      .securityId(FUTURE_SECURITY_ID_MAR)
      .deliveryBasket(UND_BOND)
      .conversionFactors(CF_MAR)
      .firstNoticeDate(FIRST_NOTICE_DATE_MAR)
      .lastNoticeDate(LAST_NOTICE_DATE_MAR)
      .firstDeliveryDate(FIRST_DELIVERY_DATE_MAR)
      .lastDeliveryDate(LAST_DELIVERY_DATE_MAR)
      .lastTradeDate(LAST_TRADING_DATE_MAR)
      .rounding(Rounding.ofDecimalPlaces(2))
      .build()
      .resolve(REF_DATA);
  private static final ResolvedBondFutureTrade FUTURE_TRADE_MAR = ResolvedBondFutureTrade.builder()
      .quantity(QUANTITY)
      .product(FUTURE_PRODUCT_MAR)
      .info(TRADE_INFO)
      .build();
  private static final double REF_PRICE_MAR = 152.25;
  // Curves
  private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR;
  private static final CurveName ISSUER_CURVE_NAME = CurveName.of("issuerCurve");
  private static final CurveName REPO_CURVE_NAME = CurveName.of("repoCurve");
  private static final LegalEntityDiscountingProvider LED_PROVIDER;
  static {
    double[] timeIssuer = new double[] {
    0.25136612021857924, 0.4972677595628415, 1.0139980537465378, 2.013998053746538, 2.857833670184894,
    3.857833670184894, 4.860655737704918, 5.857833670184894, 7.104409012650647, 7.857833670184894, 8.857923497267759,
    9.863313122239688, 14.857833670184894, 19.857833670184895, 29.857833670184895, 39.11262819073284 };
  double[] rateIssuer = new double[] {-0.0013117084834668065, -0.0010851901424876163, -0.0020906775838723216,
    -0.0022137102045172784, -0.0022695678374162888, -0.0023424568490920798, -0.0021603059162879916,
    -0.0021667343131861225, -0.0018285921969274823, -0.001355094018965514, -6.763044056712535E-4,
    1.9555294306801752E-4, 0.003944125562941363, 0.008054233458390252, 0.012276105941434846, 0.013537766297065804 };
  double[] timeRepo = new double[] {
    0.00273224043715847, 0.01912568306010929, 0.040983606557377046, 0.05737704918032787, 0.07923497267759563,
    0.2459016393442623, 0.4972677595628415, 1.0002994236095515 };
  double[] rateRepo = new double[] {2.599662058772748E-4, -8.403529976927196E-4, -0.0010105103936934236,
    -0.0011506617573950931, -0.0012708071334455143, -0.00146106683851595, -0.0014710815100096722, -0.001481096281798276 };
    CurveMetadata metaIssuer = Curves.zeroRates(ISSUER_CURVE_NAME, ACT_ACT_ISDA);
    InterpolatedNodalCurve curveIssuer = InterpolatedNodalCurve.of(
        metaIssuer, DoubleArray.copyOf(timeIssuer), DoubleArray.copyOf(rateIssuer), INTERPOLATOR);
    DiscountFactors dscIssuer = ZeroRateDiscountFactors.of(JPY, VALUATION, curveIssuer);
    CurveMetadata metaRepo = Curves.zeroRates(REPO_CURVE_NAME, ACT_ACT_ISDA);
    InterpolatedNodalCurve curve =
        InterpolatedNodalCurve.of(metaRepo, DoubleArray.copyOf(timeRepo), DoubleArray.copyOf(rateRepo), INTERPOLATOR);
    DiscountFactors dscRepo = ZeroRateDiscountFactors.of(JPY, VALUATION, curve);
    LED_PROVIDER = ImmutableLegalEntityDiscountingProvider.builder()
        .issuerCurves(ImmutableMap.<Pair<LegalEntityGroup, Currency>, DiscountFactors>of(
            Pair.<LegalEntityGroup, Currency>of(GROUP_ISSUER, JPY), dscIssuer))
        .issuerCurveGroups(ImmutableMap.<StandardId, LegalEntityGroup>of(ISSUER_ID, GROUP_ISSUER))
        .repoCurves(ImmutableMap.<Pair<RepoGroup, Currency>, DiscountFactors>of(
            Pair.<RepoGroup, Currency>of(GROUP_REPO, JPY), dscRepo))
        .repoCurveGroups(ImmutableMap.<StandardId, RepoGroup>of(ISSUER_ID, GROUP_REPO))
        .build();
  }
  // Pricers
  private static final DiscountingBondFutureProductPricer PRODUCT_PRICER = DiscountingBondFutureProductPricer.DEFAULT;
  private static final DiscountingBondFutureTradePricer TRADE_PRICER = DiscountingBondFutureTradePricer.DEFAULT;

  public void price() {
    // March
    double priceMar = PRODUCT_PRICER.price(FUTURE_PRODUCT_MAR, LED_PROVIDER) * HUNDRED;
    double priceMarRounded = FUTURE_PRODUCT_MAR.getRounding().round(priceMar);
    assertEquals(priceMar, 151.83071796298776, TOL * HUNDRED);
    assertEquals(priceMarRounded, 151.83, TOL * HUNDRED);
    // June
    double priceJun = PRODUCT_PRICER.price(FUTURE_PRODUCT_JUN, LED_PROVIDER) * HUNDRED;
    double priceJunRounded = FUTURE_PRODUCT_JUN.getRounding().round(priceJun);
    assertEquals(priceJun, 151.25027452317593, TOL * HUNDRED);
    assertEquals(priceJunRounded, 151.25, TOL * HUNDRED);
    // September
    double priceSep = PRODUCT_PRICER.price(FUTURE_PRODUCT_SEP, LED_PROVIDER) * HUNDRED;
    double priceSepRounded = FUTURE_PRODUCT_SEP.getRounding().round(priceSep);
    assertEquals(priceSep, 151.08452213883535, TOL * HUNDRED);
    assertEquals(priceSepRounded, 151.08, TOL * HUNDRED);
  }

  public void priceSensitivity() {
    // March
    PointSensitivities pointMar =
        PRODUCT_PRICER.priceSensitivity(FUTURE_PRODUCT_MAR, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiMar = LED_PROVIDER.parameterSensitivity(pointMar);
    double[] sensiIssuerMar = new double[] {-4.795692708445902E-6, -2.0781215861310126E-5, -7.730767169573405E-5,
      -1.6071777740512183E-4, -2.3044416935116369E-4, -3.333307694739688E-4, -4.263036155523118E-4,
      -5.685365085703306E-4, -0.10407934097674876, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoMar = new double[] {0.0, 0.0, 0.0, 0.0, 0.001370140084809201, 3.3554451056551886E-4, 0.0, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerMar, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoMar, TOL));
    // June
    PointSensitivities pointJun =
        PRODUCT_PRICER.priceSensitivity(FUTURE_PRODUCT_JUN, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiJun = LED_PROVIDER.parameterSensitivity(pointJun);
    double[] sensiIssuerJun = new double[] {-1.1453989553600325E-5, -2.348926498286566E-5, -1.0106640809190963E-4,
      -1.9509367993719023E-4, -3.132622179286758E-4, -4.395002117284386E-4, -5.572262990208806E-4,
      -7.858225833901946E-4, -0.07087170775675304, -0.03539736978075175, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoJun = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.003012223890022257, 0.0024215917547237764, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerJun, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoJun, TOL));
    // September
    PointSensitivities pointSep =
        PRODUCT_PRICER.priceSensitivity(FUTURE_PRODUCT_SEP, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiSep = LED_PROVIDER.parameterSensitivity(pointSep);
    double[] sensiIssuerSep = new double[] {-6.287268294968501E-6, -2.7244672992830814E-5, -1.0135221390528455E-4,
      -2.1070486533414349E-4, -3.021178394458564E-4, -4.370046427203812E-4, -5.588942763935072E-4,
      -7.453650144370277E-4, -0.03687605192905092, -0.07313888023068209, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoSep = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.007209056180693214, 0.0020653493968426154 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerSep, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoSep, TOL));
  }

  public void presentValue() {
    // March
    CurrencyAmount pvMar = TRADE_PRICER.presentValue(FUTURE_TRADE_MAR, LED_PROVIDER, REF_PRICE_MAR * ONE_PERCENT);
    assertEquals(pvMar.getAmount(), -419282.03701224923, TOL * NOTIONAL);
    // June
    CurrencyAmount pvJun = TRADE_PRICER.presentValue(FUTURE_TRADE_JUN, LED_PROVIDER, REF_PRICE_JUN * ONE_PERCENT);
    assertEquals(pvJun.getAmount(), -479725.4768240452, TOL * NOTIONAL);
    // September
    CurrencyAmount pvSep = TRADE_PRICER.presentValue(FUTURE_TRADE_SEP, LED_PROVIDER, REF_PRICE_SEP * ONE_PERCENT);
    assertEquals(pvSep.getAmount(), -525477.8611646593, TOL * NOTIONAL);
  }

  public void presentValueSensitivity() {
    // March
    PointSensitivities pointMar =
        TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE_MAR, LED_PROVIDER).multipliedBy(ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiMar = LED_PROVIDER.parameterSensitivity(pointMar);
    double[] sensiIssuerMar = new double[] {-4.795692708445902, -20.78121586131013, -77.30767169573404,
      -160.71777740512184, -230.44416935116368, -333.3307694739688, -426.3036155523117, -568.5365085703306,
      -104079.34097674876, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoMar = new double[] {0.0, 0.0, 0.0, 0.0, 1370.1400848092012, 335.54451056551886, 0.0, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerMar, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoMar, TOL));
    // June
    PointSensitivities pointJun =
        TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE_JUN, LED_PROVIDER).multipliedBy(ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiJun = LED_PROVIDER.parameterSensitivity(pointJun);
    double[] sensiIssuerJun = new double[] {-11.453989553600326, -23.489264982865656, -101.06640809190962,
      -195.09367993719025, -313.2622179286758, -439.5002117284386, -557.2262990208807, -785.8225833901945,
      -70871.70775675304, -35397.369780751746, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoJun = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 3012.223890022257, 2421.5917547237764, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerJun, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoJun, TOL));
    // September
    PointSensitivities pointSep =
        TRADE_PRICER.presentValueSensitivity(FUTURE_TRADE_SEP, LED_PROVIDER).multipliedBy(ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiSep = LED_PROVIDER.parameterSensitivity(pointSep);
    double[] sensiIssuerSep = new double[] {-6.287268294968501, -27.244672992830814, -101.35221390528456,
      -210.7048653341435, -302.1178394458564, -437.0046427203812, -558.8942763935072, -745.3650144370276,
      -36876.05192905092, -73138.88023068209, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoSep = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 7209.056180693215, 2065.3493968426155 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerSep, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoSep, TOL));
  }

  public void parSpread() {
    // March
    double psMar = TRADE_PRICER.parSpread(FUTURE_TRADE_MAR, LED_PROVIDER, REF_PRICE_MAR * ONE_PERCENT) * HUNDRED;
    assertEquals(psMar, -0.4192820370122474, TOL * HUNDRED);
    // June
    double psJun = TRADE_PRICER.parSpread(FUTURE_TRADE_JUN, LED_PROVIDER, REF_PRICE_JUN * ONE_PERCENT) * HUNDRED;
    assertEquals(psJun, -0.47972547682404443, TOL * HUNDRED);
    // September
    double psSep = TRADE_PRICER.parSpread(FUTURE_TRADE_SEP, LED_PROVIDER, REF_PRICE_SEP * ONE_PERCENT) * HUNDRED;
    assertEquals(psSep, -0.5254778611646582, TOL * HUNDRED);
  }

  public void parSpreadSensitivity() {
    // March
    PointSensitivities pointMar =
        TRADE_PRICER.parSpreadSensitivity(FUTURE_TRADE_MAR, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiMar = LED_PROVIDER.parameterSensitivity(pointMar);
    double[] sensiIssuerMar = new double[] {-4.795692708445902E-6, -2.0781215861310126E-5, -7.730767169573405E-5,
      -1.6071777740512183E-4, -2.3044416935116369E-4, -3.333307694739688E-4, -4.263036155523118E-4,
      -5.685365085703306E-4, -0.10407934097674876, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoMar = new double[] {0.0, 0.0, 0.0, 0.0, 0.001370140084809201, 3.3554451056551886E-4, 0.0, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerMar, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiMar.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoMar, TOL));
    // June
    PointSensitivities pointJun =
        TRADE_PRICER.parSpreadSensitivity(FUTURE_TRADE_JUN, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiJun = LED_PROVIDER.parameterSensitivity(pointJun);
    double[] sensiIssuerJun = new double[] {-1.1453989553600325E-5, -2.348926498286566E-5, -1.0106640809190963E-4,
      -1.9509367993719023E-4, -3.132622179286758E-4, -4.395002117284386E-4, -5.572262990208806E-4,
      -7.858225833901946E-4, -0.07087170775675304, -0.03539736978075175, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoJun = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.003012223890022257, 0.0024215917547237764, 0.0 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerJun, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiJun.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoJun, TOL));
    // September
    PointSensitivities pointSep =
        TRADE_PRICER.parSpreadSensitivity(FUTURE_TRADE_SEP, LED_PROVIDER).multipliedBy(HUNDRED * ONE_BASIS_POINT);
    CurrencyParameterSensitivities sensiSep = LED_PROVIDER.parameterSensitivity(pointSep);
    double[] sensiIssuerSep = new double[] {-6.287268294968501E-6, -2.7244672992830814E-5, -1.0135221390528455E-4,
      -2.1070486533414349E-4, -3.021178394458564E-4, -4.370046427203812E-4, -5.588942763935072E-4,
      -7.453650144370277E-4, -0.03687605192905092, -0.07313888023068209, -0.0, -0.0, -0.0, -0.0, -0.0, -0.0 };
    double[] sensiRepoSep = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.007209056180693214, 0.0020653493968426154 };
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(ISSUER_CURVE_NAME, JPY).getSensitivity().toArray(), sensiIssuerSep, TOL));
    assertTrue(DoubleArrayMath.fuzzyEquals(
        sensiSep.getSensitivity(REPO_CURVE_NAME, JPY).getSensitivity().toArray(), sensiRepoSep, TOL));
  }

}
