예제 #1
0
public class CalibrationEurStandard {

  private static final DayCount CURVE_DC = ACT_365F;
  private static final LocalDateDoubleTimeSeries TS_EMTPY = LocalDateDoubleTimeSeries.empty();

  private static final String SCHEME = "CALIBRATION";

  /** Curve names */
  private static final String DSCON_NAME = "EUR_EONIA_EOD";

  public static final CurveName DSCON_CURVE_NAME = CurveName.of(DSCON_NAME);
  private static final String FWD3_NAME = "EUR_EURIBOR_3M";
  public static final CurveName FWD3_CURVE_NAME = CurveName.of(FWD3_NAME);
  private static final String FWD6_NAME = "EUR_EURIBOR_6M";
  public static final CurveName FWD6_CURVE_NAME = CurveName.of(FWD6_NAME);
  private static final String CURVE_GROUP_NAME_STR = "EUR-DSCON-EURIBOR3M-EURIBOR6M";
  private static final CurveGroupName CURVE_GROUP_NAME = CurveGroupName.of(CURVE_GROUP_NAME_STR);
  /** Curves associations to currencies and indices. */
  private static final Map<CurveName, Currency> DSC_NAMES = new HashMap<>();

  private static final Map<CurveName, Set<Index>> IDX_NAMES = new HashMap<>();
  private static final Map<Index, LocalDateDoubleTimeSeries> TS = new HashMap<>();

  static {
    DSC_NAMES.put(DSCON_CURVE_NAME, EUR);
    Set<Index> eurEoniaSet = new HashSet<>();
    eurEoniaSet.add(EUR_EONIA);
    IDX_NAMES.put(DSCON_CURVE_NAME, eurEoniaSet);
    Set<Index> eurEuribor3Set = new HashSet<>();
    eurEuribor3Set.add(EUR_EURIBOR_3M);
    IDX_NAMES.put(FWD3_CURVE_NAME, eurEuribor3Set);
    Set<Index> eurEuriabor6Set = new HashSet<>();
    eurEuriabor6Set.add(EUR_EURIBOR_6M);
    IDX_NAMES.put(FWD6_CURVE_NAME, eurEuriabor6Set);
    TS.put(EUR_EURIBOR_3M, TS_EMTPY);
    TS.put(EUR_EURIBOR_6M, TS_EMTPY);
    TS.put(EUR_EONIA, TS_EMTPY);
  }

  private static final CurveInterpolator INTERPOLATOR_LINEAR = new LinearInterpolator1D();
  private static final CurveExtrapolator EXTRAPOLATOR_FLAT = new FlatExtrapolator1D();

  private static final CalibrationMeasures CALIBRATION_MEASURES = CalibrationMeasures.DEFAULT;
  private static final CurveCalibrator CALIBRATOR =
      CurveCalibrator.of(1e-9, 1e-9, 100, CALIBRATION_MEASURES);

  public static ImmutableRatesProvider calibrateEurStandard(
      LocalDate valuationDate,
      double[] dscOisQuotes,
      Period[] dscOisTenors,
      double fwd3FixingQuote,
      double[] fwd3FraQuotes,
      double[] fwd3IrsQuotes,
      Period[] fwd3FraTenors,
      Period[] fwd3IrsTenors,
      double fwd6FixingQuote,
      double[] fwd6FraQuotes,
      double[] fwd6IrsQuotes,
      Period[] fwd6FraTenors,
      Period[] fwd6IrsTenors) {
    /* Curve Discounting/EUR-EONIA */
    String[] dscIdValues = dscIdValues(dscOisTenors);
    /* Curve EUR-EURIBOR-3M */
    double[] fwd3MarketQuotes = fwdMarketQuotes(fwd3FixingQuote, fwd3FraQuotes, fwd3IrsQuotes);
    String[] fwd3IdValues =
        fwdIdValue(3, fwd3FixingQuote, fwd3FraQuotes, fwd3IrsQuotes, fwd3FraTenors, fwd3IrsTenors);
    /* Curve EUR-EURIBOR-6M */
    double[] fwd6MarketQuotes = fwdMarketQuotes(fwd6FixingQuote, fwd6FraQuotes, fwd6IrsQuotes);
    String[] fwd6IdValues =
        fwdIdValue(6, fwd6FixingQuote, fwd6FraQuotes, fwd6IrsQuotes, fwd6FraTenors, fwd6IrsTenors);
    /* All quotes for the curve calibration */
    ObservableValues allQuotes =
        allQuotes(
            dscOisQuotes,
            dscIdValues,
            fwd3MarketQuotes,
            fwd3IdValues,
            fwd6MarketQuotes,
            fwd6IdValues);
    /* All nodes by groups. */
    CurveGroupDefinition config =
        config(
            dscOisTenors,
            dscIdValues,
            fwd3FraTenors,
            fwd3IrsTenors,
            fwd3IdValues,
            fwd6FraTenors,
            fwd6IrsTenors,
            fwd6IdValues);
    /* Results */
    return CALIBRATOR.calibrate(config, valuationDate, allQuotes, TS, FxMatrix.empty());
  }

  public static String[] dscIdValues(Period[] dscOisTenors) {
    String[] dscIdValues = new String[dscOisTenors.length];
    for (int i = 0; i < dscOisTenors.length; i++) {
      dscIdValues[i] = "OIS" + dscOisTenors[i].toString();
    }
    return dscIdValues;
  }

  public static String[] fwdIdValue(
      int tenor,
      double fwdFixingQuote,
      double[] fwdFraQuotes,
      double[] fwdIrsQuotes,
      Period[] fwdFraTenors,
      Period[] fwdIrsTenors) {
    String[] fwdIdValue = new String[1 + fwdFraQuotes.length + fwdIrsQuotes.length];
    fwdIdValue[0] = "FIXING" + tenor + "M";
    for (int i = 0; i < fwdFraQuotes.length; i++) {
      fwdIdValue[i + 1] =
          "FRA" + fwdFraTenors[i].toString() + "x" + fwdFraTenors[i].plusMonths(tenor).toString();
    }
    for (int i = 0; i < fwdIrsQuotes.length; i++) {
      fwdIdValue[i + 1 + fwdFraQuotes.length] = "IRS" + fwdIrsTenors[i].toString();
    }
    return fwdIdValue;
  }

  public static double[] fwdMarketQuotes(
      double fwdFixingQuote, double[] fwdFraQuotes, double[] fwdIrsQuotes) {
    int fwdNbFraNodes = fwdFraQuotes.length;
    int fwdNbIrsNodes = fwdIrsQuotes.length;
    int fwdNbNodes = 1 + fwdNbFraNodes + fwdNbIrsNodes;
    double[] fwdMarketQuotes = new double[fwdNbNodes];
    fwdMarketQuotes[0] = fwdFixingQuote;
    System.arraycopy(fwdFraQuotes, 0, fwdMarketQuotes, 1, fwdNbFraNodes);
    System.arraycopy(fwdIrsQuotes, 0, fwdMarketQuotes, 1 + fwdNbFraNodes, fwdNbIrsNodes);
    return fwdMarketQuotes;
  }

  public static CurveGroupDefinition config(
      Period[] dscOisTenors,
      String[] dscIdValues,
      Period[] fwd3FraTenors,
      Period[] fwd3IrsTenors,
      String[] fwd3IdValues,
      Period[] fwd6FraTenors,
      Period[] fwd6IrsTenors,
      String[] fwd6IdValues) {
    CurveNode[] dscNodes = new CurveNode[dscOisTenors.length];
    for (int i = 0; i < dscOisTenors.length; i++) {
      dscNodes[i] =
          FixedOvernightSwapCurveNode.of(
              FixedOvernightSwapTemplate.of(
                  Period.ZERO, Tenor.of(dscOisTenors[i]), EUR_FIXED_1Y_EONIA_OIS),
              QuoteKey.of(StandardId.of(SCHEME, dscIdValues[i])));
    }
    CurveNode[] fwd3Nodes = new CurveNode[fwd3IdValues.length];
    fwd3Nodes[0] =
        IborFixingDepositCurveNode.of(
            IborFixingDepositTemplate.of(EUR_EURIBOR_3M),
            QuoteKey.of(StandardId.of(SCHEME, fwd3IdValues[0])));
    for (int i = 0; i < fwd3FraTenors.length; i++) {
      fwd3Nodes[i + 1] =
          FraCurveNode.of(
              FraTemplate.of(fwd3FraTenors[i], EUR_EURIBOR_3M),
              QuoteKey.of(StandardId.of(SCHEME, fwd3IdValues[i + 1])));
    }
    for (int i = 0; i < fwd3IrsTenors.length; i++) {
      fwd3Nodes[i + 1 + fwd3FraTenors.length] =
          FixedIborSwapCurveNode.of(
              FixedIborSwapTemplate.of(
                  Period.ZERO, Tenor.of(fwd3IrsTenors[i]), EUR_FIXED_1Y_EURIBOR_3M),
              QuoteKey.of(StandardId.of(SCHEME, fwd3IdValues[i + 1])));
    }
    CurveNode[] fwd6Nodes = new CurveNode[fwd6IdValues.length];
    fwd6Nodes[0] =
        IborFixingDepositCurveNode.of(
            IborFixingDepositTemplate.of(EUR_EURIBOR_6M),
            QuoteKey.of(StandardId.of(SCHEME, fwd6IdValues[0])));
    for (int i = 0; i < fwd6FraTenors.length; i++) {
      fwd6Nodes[i + 1] =
          FraCurveNode.of(
              FraTemplate.of(fwd6FraTenors[i], EUR_EURIBOR_6M),
              QuoteKey.of(StandardId.of(SCHEME, fwd6IdValues[i + 1])));
    }
    for (int i = 0; i < fwd6IrsTenors.length; i++) {
      fwd6Nodes[i + 1 + fwd6FraTenors.length] =
          FixedIborSwapCurveNode.of(
              FixedIborSwapTemplate.of(
                  Period.ZERO, Tenor.of(fwd6IrsTenors[i]), EUR_FIXED_1Y_EURIBOR_6M),
              QuoteKey.of(StandardId.of(SCHEME, fwd6IdValues[i + 1])));
    }
    InterpolatedNodalCurveDefinition DSC_CURVE_DEFN =
        InterpolatedNodalCurveDefinition.builder()
            .name(DSCON_CURVE_NAME)
            .xValueType(ValueType.YEAR_FRACTION)
            .yValueType(ValueType.ZERO_RATE)
            .dayCount(CURVE_DC)
            .interpolator(INTERPOLATOR_LINEAR)
            .extrapolatorLeft(EXTRAPOLATOR_FLAT)
            .extrapolatorRight(EXTRAPOLATOR_FLAT)
            .nodes(dscNodes)
            .build();
    InterpolatedNodalCurveDefinition FWD3_CURVE_DEFN =
        InterpolatedNodalCurveDefinition.builder()
            .name(FWD3_CURVE_NAME)
            .xValueType(ValueType.YEAR_FRACTION)
            .yValueType(ValueType.ZERO_RATE)
            .dayCount(CURVE_DC)
            .interpolator(INTERPOLATOR_LINEAR)
            .extrapolatorLeft(EXTRAPOLATOR_FLAT)
            .extrapolatorRight(EXTRAPOLATOR_FLAT)
            .nodes(fwd3Nodes)
            .build();
    InterpolatedNodalCurveDefinition FWD6_CURVE_DEFN =
        InterpolatedNodalCurveDefinition.builder()
            .name(FWD6_CURVE_NAME)
            .xValueType(ValueType.YEAR_FRACTION)
            .yValueType(ValueType.ZERO_RATE)
            .dayCount(CURVE_DC)
            .interpolator(INTERPOLATOR_LINEAR)
            .extrapolatorLeft(EXTRAPOLATOR_FLAT)
            .extrapolatorRight(EXTRAPOLATOR_FLAT)
            .nodes(fwd6Nodes)
            .build();
    return CurveGroupDefinition.builder()
        .name(CURVE_GROUP_NAME)
        .addCurve(DSC_CURVE_DEFN, EUR, EUR_EONIA)
        .addForwardCurve(FWD3_CURVE_DEFN, EUR_EURIBOR_3M)
        .addForwardCurve(FWD6_CURVE_DEFN, EUR_EURIBOR_6M)
        .build();
  }

  public static ObservableValues allQuotes(
      double[] dscOisQuotes,
      String[] dscIdValues,
      double[] fwd3MarketQuotes,
      String[] fwd3IdValue,
      double[] fwd6MarketQuotes,
      String[] fwd6IdValue) {
    /* All quotes for the curve calibration */
    Map<ObservableKey, Double> allQuotes = new HashMap<>();
    for (int i = 0; i < dscOisQuotes.length; i++) {
      allQuotes.put(QuoteKey.of(StandardId.of(SCHEME, dscIdValues[i])), dscOisQuotes[i]);
    }
    for (int i = 0; i < fwd3MarketQuotes.length; i++) {
      allQuotes.put(QuoteKey.of(StandardId.of(SCHEME, fwd3IdValue[i])), fwd3MarketQuotes[i]);
    }
    for (int i = 0; i < fwd6MarketQuotes.length; i++) {
      allQuotes.put(QuoteKey.of(StandardId.of(SCHEME, fwd6IdValue[i])), fwd6MarketQuotes[i]);
    }
    return ObservableValues.of(allQuotes);
  }
}
/**
 * Test for curve calibration in USD and EUR. The USD curve is obtained by OIS and the EUR one by FX
 * Swaps from USD.
 */
@Test
public class CalibrationZeroRateUsdEur2OisFxTest {

  private static final LocalDate VAL_DATE = LocalDate.of(2015, 11, 2);

  private static final CurveInterpolator INTERPOLATOR_LINEAR = CurveInterpolators.LINEAR;
  private static final CurveExtrapolator EXTRAPOLATOR_FLAT = CurveExtrapolators.FLAT;
  private static final DayCount CURVE_DC = ACT_365F;

  private static final String SCHEME = "CALIBRATION";

  /** Curve names */
  private static final String USD_DSCON_STR = "USD-DSCON-OIS";

  private static final CurveName USD_DSCON_CURVE_NAME = CurveName.of(USD_DSCON_STR);
  private static final String EUR_DSC_STR = "EUR-DSC-FX";
  private static final CurveName EUR_DSC_CURVE_NAME = CurveName.of(EUR_DSC_STR);
  /** Curves associations to currencies and indices. */
  private static final Map<CurveName, Currency> DSC_NAMES = new HashMap<>();

  private static final Map<CurveName, Set<Index>> IDX_NAMES = new HashMap<>();
  private static final Map<Index, LocalDateDoubleTimeSeries> TS = new HashMap<>();

  static {
    DSC_NAMES.put(USD_DSCON_CURVE_NAME, USD);
    Set<Index> usdFedFundSet = new HashSet<>();
    usdFedFundSet.add(USD_FED_FUND);
    IDX_NAMES.put(USD_DSCON_CURVE_NAME, usdFedFundSet);
  }

  /** Data FX * */
  private static final FxRate FX_RATE_EUR_USD = FxRate.of(EUR, USD, 1.10);
  /** Data for USD-DSCON curve */
  /* Market values */
  private static final double[] USD_DSC_MARKET_QUOTES =
      new double[] {
        0.0016, 0.0022, 0.0013, 0.0016, 0.0020, 0.0026, 0.0033, 0.0039, 0.0053, 0.0066, 0.0090,
        0.0111
      };

  private static final int USD_DSC_NB_NODES = USD_DSC_MARKET_QUOTES.length;
  private static final String[] USD_DSC_ID_VALUE =
      new String[] {
        "USD-ON",
        "USD-TN",
        "USD-OIS-1M",
        "USD-OIS-2M",
        "USD-OIS-3M",
        "USD-OIS-6M",
        "USD-OIS-9M",
        "USD-OIS-1Y",
        "USD-OIS-18M",
        "USD-OIS-2Y",
        "USD-OIS-3Y",
        "USD-OIS-4Y"
      };
  /* Nodes */
  private static final CurveNode[] USD_DSC_NODES = new CurveNode[USD_DSC_NB_NODES];
  /* Tenors */
  private static final int[] USD_DSC_DEPO_OFFSET = new int[] {0, 1};
  private static final int USD_DSC_NB_DEPO_NODES = USD_DSC_DEPO_OFFSET.length;
  private static final Period[] USD_DSC_OIS_TENORS =
      new Period[] {
        Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6),
            Period.ofMonths(9),
        Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3),
            Period.ofYears(4)
      };
  private static final int USD_DSC_NB_OIS_NODES = USD_DSC_OIS_TENORS.length;

  static {
    USD_DSC_NODES[0] =
        TermDepositCurveNode.of(
            TermDepositTemplate.of(Period.ofDays(1), USD_DEPOSIT_T0),
            QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[0])));
    USD_DSC_NODES[1] =
        TermDepositCurveNode.of(
            TermDepositTemplate.of(Period.ofDays(1), USD_DEPOSIT_T1),
            QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[1])));
    for (int i = 0; i < USD_DSC_NB_OIS_NODES; i++) {
      USD_DSC_NODES[USD_DSC_NB_DEPO_NODES + i] =
          FixedOvernightSwapCurveNode.of(
              FixedOvernightSwapTemplate.of(
                  Period.ZERO, Tenor.of(USD_DSC_OIS_TENORS[i]), USD_FIXED_1Y_FED_FUND_OIS),
              QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[USD_DSC_NB_DEPO_NODES + i])));
    }
  }
  /** Data for EUR-DSC curve */
  /* Market values */
  private static final double[] EUR_DSC_MARKET_QUOTES =
      new double[] {
        0.0004, 0.0012, 0.0019, 0.0043, 0.0074,
        0.0109, 0.0193, 0.0294, 0.0519, 0.0757
      };

  private static final int EUR_DSC_NB_NODES = EUR_DSC_MARKET_QUOTES.length;
  private static final String[] EUR_DSC_ID_VALUE =
      new String[] {
        "EUR-USD-FX-1M", "EUR-USD-FX-2M", "EUR-USD-FX-3M", "EUR-USD-FX-6M", "EUR-USD-FX-9M",
        "EUR-USD-FX-1Y", "EUR-USD-FX-18M", "EUR-USD-FX-2Y", "EUR-USD-FX-3Y", "EUR-USD-FX-4Y"
      };
  /* Nodes */
  private static final CurveNode[] EUR_DSC_NODES = new CurveNode[EUR_DSC_NB_NODES];
  /* Tenors */
  private static final Period[] EUR_DSC_FX_TENORS =
      new Period[] {
        Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(6),
            Period.ofMonths(9),
        Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3),
            Period.ofYears(4)
      };
  private static final int EUR_DSC_NB_FX_NODES = EUR_DSC_FX_TENORS.length;

  static {
    for (int i = 0; i < EUR_DSC_NB_FX_NODES; i++) {
      EUR_DSC_NODES[i] =
          FxSwapCurveNode.of(
              FxSwapTemplate.of(EUR_DSC_FX_TENORS[i], EUR_USD),
              QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i])));
    }
  }

  /** All quotes for the curve calibration */
  private static final ImmutableMarketData ALL_QUOTES;

  static {
    Map<MarketDataKey<?>, Object> map = new HashMap<>();
    for (int i = 0; i < USD_DSC_NB_NODES; i++) {
      map.put(QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])), USD_DSC_MARKET_QUOTES[i]);
    }
    for (int i = 0; i < EUR_DSC_NB_NODES; i++) {
      map.put(QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i])), EUR_DSC_MARKET_QUOTES[i]);
    }
    map.put(FxRateKey.of(EUR, USD), FX_RATE_EUR_USD);
    ALL_QUOTES = ImmutableMarketData.of(map);
  }

  private static final DiscountingSwapProductPricer SWAP_PRICER =
      DiscountingSwapProductPricer.DEFAULT;
  private static final DiscountingTermDepositProductPricer DEPO_PRICER =
      DiscountingTermDepositProductPricer.DEFAULT;
  private static final DiscountingFxSwapProductPricer FX_PRICER =
      DiscountingFxSwapProductPricer.DEFAULT;
  private static final MarketQuoteSensitivityCalculator MQC =
      MarketQuoteSensitivityCalculator.DEFAULT;

  private static final CalibrationMeasures CALIBRATION_MEASURES = CalibrationMeasures.DEFAULT;
  private static final CurveCalibrator CALIBRATOR =
      CurveCalibrator.of(1e-9, 1e-9, 100, CALIBRATION_MEASURES);

  // Constants
  private static final double TOLERANCE_PV = 1.0E-6;
  private static final double TOLERANCE_PV_DELTA = 1.0E+3;

  private static final CurveGroupName CURVE_GROUP_NAME = CurveGroupName.of("USD-DSCON-EUR-DSC");
  private static final InterpolatedNodalCurveDefinition USD_DSC_CURVE_DEFN =
      InterpolatedNodalCurveDefinition.builder()
          .name(USD_DSCON_CURVE_NAME)
          .xValueType(ValueType.YEAR_FRACTION)
          .yValueType(ValueType.ZERO_RATE)
          .dayCount(CURVE_DC)
          .interpolator(INTERPOLATOR_LINEAR)
          .extrapolatorLeft(EXTRAPOLATOR_FLAT)
          .extrapolatorRight(EXTRAPOLATOR_FLAT)
          .nodes(USD_DSC_NODES)
          .build();
  private static final InterpolatedNodalCurveDefinition EUR_DSC_CURVE_DEFN =
      InterpolatedNodalCurveDefinition.builder()
          .name(EUR_DSC_CURVE_NAME)
          .xValueType(ValueType.YEAR_FRACTION)
          .yValueType(ValueType.ZERO_RATE)
          .dayCount(CURVE_DC)
          .interpolator(INTERPOLATOR_LINEAR)
          .extrapolatorLeft(EXTRAPOLATOR_FLAT)
          .extrapolatorRight(EXTRAPOLATOR_FLAT)
          .nodes(EUR_DSC_NODES)
          .build();
  private static final CurveGroupDefinition CURVE_GROUP_CONFIG =
      CurveGroupDefinition.builder()
          .name(CURVE_GROUP_NAME)
          .addCurve(USD_DSC_CURVE_DEFN, USD, USD_FED_FUND)
          .addDiscountCurve(EUR_DSC_CURVE_DEFN, EUR)
          .build();

  // -------------------------------------------------------------------------
  public void calibration_present_value_oneGroup() {
    ImmutableRatesProvider result =
        CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ALL_QUOTES, TS);
    assertPresentValue(result);
  }

  private void assertPresentValue(ImmutableRatesProvider result) {
    // Test PV USD;
    List<Trade> usdTrades = new ArrayList<>();
    for (int i = 0; i < USD_DSC_NODES.length; i++) {
      usdTrades.add(USD_DSC_NODES[i].trade(VAL_DATE, ALL_QUOTES));
    }
    // Depo
    for (int i = 0; i < USD_DSC_NB_DEPO_NODES; i++) {
      CurrencyAmount pvDep =
          DEPO_PRICER.presentValue(((TermDepositTrade) usdTrades.get(i)).getProduct(), result);
      assertEquals(pvDep.getAmount(), 0.0, TOLERANCE_PV);
    }
    // OIS
    for (int i = 0; i < USD_DSC_NB_OIS_NODES; i++) {
      MultiCurrencyAmount pvOis =
          SWAP_PRICER.presentValue(
              ((SwapTrade) usdTrades.get(USD_DSC_NB_DEPO_NODES + i)).getProduct(), result);
      assertEquals(pvOis.getAmount(USD).getAmount(), 0.0, TOLERANCE_PV);
    }
    // Test PV EUR;
    List<Trade> eurTrades = new ArrayList<>();
    for (int i = 0; i < EUR_DSC_NODES.length; i++) {
      eurTrades.add(EUR_DSC_NODES[i].trade(VAL_DATE, ALL_QUOTES));
    }
    // Depo
    for (int i = 0; i < EUR_DSC_NB_FX_NODES; i++) {
      MultiCurrencyAmount pvFx =
          FX_PRICER.presentValue(((FxSwapTrade) eurTrades.get(i)).getProduct(), result);
      assertEquals(pvFx.convertedTo(USD, result).getAmount(), 0.0, TOLERANCE_PV);
    }
  }

  public void calibration_market_quote_sensitivity_one_group() {
    double shift = 1.0E-6;
    Function<MarketData, ImmutableRatesProvider> f =
        ov -> CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ov, TS);
    calibration_market_quote_sensitivity_check(f, shift);
  }

  private void calibration_market_quote_sensitivity_check(
      Function<MarketData, ImmutableRatesProvider> calibrator, double shift) {

    double notional = 100_000_000.0;
    double fx = 1.1111;
    double fxPts = 0.0012;
    FxSwapTrade trade =
        EUR_USD.toTrade(
            VAL_DATE, Period.ofWeeks(6), Period.ofMonths(5), BuySell.BUY, notional, fx, fxPts);
    ImmutableRatesProvider result =
        CALIBRATOR.calibrate(CURVE_GROUP_CONFIG, VAL_DATE, ALL_QUOTES, TS);
    PointSensitivities pts = FX_PRICER.presentValueSensitivity(trade.getProduct(), result);
    CurveCurrencyParameterSensitivities ps = result.curveParameterSensitivity(pts);
    CurveCurrencyParameterSensitivities mqs = MQC.sensitivity(ps, result);
    double pvUsd = FX_PRICER.presentValue(trade.getProduct(), result).getAmount(USD).getAmount();
    double pvEur = FX_PRICER.presentValue(trade.getProduct(), result).getAmount(EUR).getAmount();
    double[] mqsUsd1Computed =
        mqs.getSensitivity(USD_DSCON_CURVE_NAME, USD).getSensitivity().toArray();
    for (int i = 0; i < USD_DSC_NB_NODES; i++) {
      Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues());
      map.put(
          QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])),
          USD_DSC_MARKET_QUOTES[i] + shift);
      ImmutableMarketData marketData = ImmutableMarketData.of(map);
      ImmutableRatesProvider rpShifted = calibrator.apply(marketData);
      double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(USD).getAmount();
      assertEquals(mqsUsd1Computed[i], (pvS - pvUsd) / shift, TOLERANCE_PV_DELTA);
    }
    double[] mqsUsd2Computed =
        mqs.getSensitivity(USD_DSCON_CURVE_NAME, EUR).getSensitivity().toArray();
    for (int i = 0; i < USD_DSC_NB_NODES; i++) {
      Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues());
      map.put(
          QuoteKey.of(StandardId.of(SCHEME, USD_DSC_ID_VALUE[i])),
          USD_DSC_MARKET_QUOTES[i] + shift);
      ImmutableMarketData ov = ImmutableMarketData.of(map);
      ImmutableRatesProvider rpShifted = calibrator.apply(ov);
      double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(EUR).getAmount();
      assertEquals(mqsUsd2Computed[i], (pvS - pvEur) / shift, TOLERANCE_PV_DELTA);
    }
    double[] mqsEur1Computed =
        mqs.getSensitivity(EUR_DSC_CURVE_NAME, USD).getSensitivity().toArray();
    for (int i = 0; i < EUR_DSC_NB_NODES; i++) {
      assertEquals(mqsEur1Computed[i], 0.0, TOLERANCE_PV_DELTA);
    }
    double[] mqsEur2Computed =
        mqs.getSensitivity(EUR_DSC_CURVE_NAME, EUR).getSensitivity().toArray();
    for (int i = 0; i < EUR_DSC_NB_NODES; i++) {
      Map<MarketDataKey<?>, Object> map = new HashMap<>(ALL_QUOTES.getValues());
      map.put(
          QuoteKey.of(StandardId.of(SCHEME, EUR_DSC_ID_VALUE[i])),
          EUR_DSC_MARKET_QUOTES[i] + shift);
      ImmutableMarketData marketData = ImmutableMarketData.of(map);
      ImmutableRatesProvider rpShifted = calibrator.apply(marketData);
      double pvS = FX_PRICER.presentValue(trade.getProduct(), rpShifted).getAmount(EUR).getAmount();
      assertEquals(mqsEur2Computed[i], (pvS - pvEur) / shift, TOLERANCE_PV_DELTA, "Node " + i);
    }
  }
}