/**
  * Returns a copy of this {@code MultipleCurrencyAmount} with all the amounts multiplied by the
  * factor.
  *
  * <p>This instance is immutable and unaffected by this method.
  *
  * @param factor The multiplicative factor.
  * @return An amount based on this with all the amounts multiplied by the factor. Not null
  */
 public MultipleCurrencyAmount multipliedBy(final double factor) {
   TreeMap<Currency, Double> map = new TreeMap<Currency, Double>();
   for (CurrencyAmount currencyAmount : this) {
     map.put(currencyAmount.getCurrency(), currencyAmount.getAmount() * factor);
   }
   return MultipleCurrencyAmount.of(map);
 }
 /**
  * Compute the the present value of a fixed payment by discounting to a parallel curve movement.
  *
  * @param payment The payment.
  * @param multicurves The multi-curve provider.
  * @return The present value.
  */
 public MultipleCurrencyAmount presentValue(
     final PaymentFixed payment, final MulticurveProviderInterface multicurves) {
   ArgumentChecker.notNull(payment, "Payment");
   ArgumentChecker.notNull(multicurves, "Multi-curves");
   final double pv =
       payment.getAmount()
           * multicurves.getDiscountFactor(payment.getCurrency(), payment.getPaymentTime());
   return MultipleCurrencyAmount.of(payment.getCurrency(), pv);
 }
 /**
  * Compute the present value of a commodity cash settle forward by discounting.
  *
  * @param forward The forward.
  * @param multicurve The commodity multi-curve provider.
  * @return The present value.
  */
 public MultipleCurrencyAmount presentValue(
     final ForwardCommodityCashSettle forward, final CommodityProviderInterface multicurve) {
   ArgumentChecker.notNull(forward, "Forward");
   ArgumentChecker.notNull(multicurve, "Multi-curves provider");
   final double fwd =
       multicurve.getForwardValue(forward.getUnderlying(), forward.getSettlementTime());
   final double df = multicurve.getDiscountFactor(forward.getCurrency(), forward.getPaymentTime());
   final double pv = forward.getNotional() * (fwd - forward.getRate()) * df;
   return MultipleCurrencyAmount.of(forward.getCurrency(), pv);
 }
 @Test
 public void presentValueDiscountingCalculator() {
   final MultipleCurrencyAmount pvSwap = SWAP_MULTI_LEG.accept(PVDC, MULTICURVES);
   MultipleCurrencyAmount pvLegs = MultipleCurrencyAmount.of(EUR, 0.0);
   for (int loopleg = 0; loopleg < NB_LEGS; loopleg++) {
     pvLegs = pvLegs.plus(SWAP_MULTI_LEG.getLegs()[loopleg].accept(PVDC, MULTICURVES));
   }
   assertEquals(
       "SwapMultileg: presentValueDiscountingCalculator",
       pvSwap.getAmount(EUR),
       pvLegs.getAmount(EUR),
       TOLERANCE_PV);
 }
 @Test
 /** Check the conversion of a multiple currency amount. */
 public void convert() {
   final FXMatrix fxMatrix = new FXMatrix();
   fxMatrix.addCurrency(EUR, USD, EUR_USD);
   fxMatrix.addCurrency(GBP, EUR, GBP_EUR);
   final double amountGBP = 1.0;
   final double amountEUR = 2.0;
   final double amountUSD = 3.0;
   MultipleCurrencyAmount amount = MultipleCurrencyAmount.of(GBP, amountGBP);
   amount = amount.plus(EUR, amountEUR);
   amount = amount.plus(USD, amountUSD);
   final CurrencyAmount totalUSDCalculated = fxMatrix.convert(amount, USD);
   final double totalUSDExpected = amountUSD + amountEUR * EUR_USD + amountGBP * GBP_EUR * EUR_USD;
   assertEquals("FXMatrix - convert", totalUSDExpected, totalUSDCalculated.getAmount(), 1.0E-10);
   assertEquals("FXMatrix - convert", USD, totalUSDCalculated.getCurrency());
 }
  @Test(enabled = false)
  /** Tests of performance. "enabled = false" for the standard testing. */
  public void performanceCurveSensitivity() {
    long startTime, endTime;
    final int nbTest = 25;
    MultipleCurrencyAmount pvMC = MultipleCurrencyAmount.of(EUR, 0.0);
    final MultipleCurrencyMulticurveSensitivity pvcsExplicit =
        METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    MultipleCurrencyMulticurveSensitivity pvcsMC = pvcsExplicit;
    final HullWhiteMonteCarloMethod methodMC =
        new HullWhiteMonteCarloMethod(
            new NormalRandomNumberGenerator(0.0, 1.0, new MersenneTwister()), NB_PATH);

    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvMC = METHOD_HW_MONTECARLO.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " swaption Hull-White Monte Carlo method ("
            + NB_PATH
            + " paths): "
            + (endTime - startTime)
            + " ms / price:"
            + pvMC.toString());
    // Performance note: HW approximation: 03-Dec-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 250
    // ms for 25 swaptions (12500 paths).
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvcsMC = methodMC.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " curve sensitivity swaption Hull-White MC method: ("
            + NB_PATH
            + " paths) "
            + (endTime - startTime)
            + " ms / risk:"
            + pvcsMC.toString());
    // Performance note: curve sensitivity (40): 03-Dec-2012: On Mac Pro 3.2 GHz Quad-Core Intel
    // Xeon: 600 ms for 25 swaptions (12500 paths).

  }
 /**
  * Compute the present value of a Ibor compounded coupon by discounting.
  *
  * @param coupon The coupon.
  * @param multicurve The multi-curve provider.
  * @return The present value.
  */
 public MultipleCurrencyAmount presentValue(
     final CouponIborCompounding coupon, final MulticurveProviderInterface multicurve) {
   ArgumentChecker.notNull(coupon, "Coupon");
   ArgumentChecker.notNull(multicurve, "Multi-curves provider");
   final int nbSubPeriod = coupon.getFixingTimes().length;
   double notionalAccrued = coupon.getNotionalAccrued();
   for (int loopsub = 0; loopsub < nbSubPeriod; loopsub++) {
     final double ratioForward =
         (1.0
             + coupon.getPaymentAccrualFactors()[loopsub]
                 * multicurve.getForwardRate(
                     coupon.getIndex(),
                     coupon.getFixingPeriodStartTimes()[loopsub],
                     coupon.getFixingPeriodEndTimes()[loopsub],
                     coupon.getFixingPeriodAccrualFactors()[loopsub]));
     notionalAccrued *= ratioForward;
   }
   final double df = multicurve.getDiscountFactor(coupon.getCurrency(), coupon.getPaymentTime());
   final double pv = (notionalAccrued - coupon.getNotional()) * df;
   return MultipleCurrencyAmount.of(coupon.getCurrency(), pv);
 }
  @Test(enabled = false)
  /** Tests of performance. "enabled = false" for the standard testing. */
  public void performance() {
    long startTime, endTime;
    final int nbTest = 1000;
    MultipleCurrencyAmount pvPayerLongExplicit = MultipleCurrencyAmount.of(EUR, 0.0);
    MultipleCurrencyAmount pvPayerLongIntegration = MultipleCurrencyAmount.of(EUR, 0.0);
    MultipleCurrencyAmount pvPayerLongApproximation = MultipleCurrencyAmount.of(EUR, 0.0);
    @SuppressWarnings("unused")
    MultipleCurrencyAmount pvPayerLongMC = MultipleCurrencyAmount.of(EUR, 0.0);
    double[] pvhws =
        METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    MultipleCurrencyMulticurveSensitivity pvcs =
        METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);

    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvPayerLongExplicit = METHOD_HW.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest + " pv swaption Hull-White explicit method: " + (endTime - startTime) + " ms");
    // Performance note: HW price: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 380 ms for
    // 10000 swaptions.
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " HW sensitivity swaption Hull-White explicit method: "
            + (endTime - startTime)
            + " ms");
    // Performance note: HW sensitivity (3): 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon:
    // 430 ms for 10000 swaptions.
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvcs = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " curve sensitivity swaption Hull-White explicit method: "
            + (endTime - startTime)
            + " ms");
    // Performance note: curve sensitivity (40): 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel
    // Xeon: 855 ms for 10000 swaptions.
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
      pvcs = METHOD_HW.presentValueCurveSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
      pvhws = METHOD_HW.presentValueHullWhiteSensitivity(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " price/delta/vega swaption Hull-White explicit method: "
            + (endTime - startTime)
            + " ms");
    // Performance note: present value/delta/vega: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel
    // Xeon: 1730 ms for 10000 swaptions.
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvPayerLongIntegration =
          METHOD_HW_INTEGRATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " swaption Hull-White numerical integration method: "
            + (endTime - startTime)
            + " ms");
    // Performance note: HW numerical integration: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel
    // Xeon: 1700 ms for 10000 swaptions.
    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvPayerLongApproximation =
          METHOD_HW_APPROXIMATION.presentValue(SWAPTION_LONG_PAYER, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest + " swaption Hull-White approximation method: " + (endTime - startTime) + " ms");
    // Performance note: HW approximation: 19-Nov-2012: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 250
    // ms for 10000 swaptions.

    startTime = System.currentTimeMillis();
    for (int looptest = 0; looptest < nbTest; looptest++) {
      pvPayerLongMC = METHOD_HW_MONTECARLO.presentValue(SWAPTION_LONG_PAYER, EUR, HW_MULTICURVES);
    }
    endTime = System.currentTimeMillis();
    System.out.println(
        nbTest
            + " swaption Hull-White Monte Carlo method ("
            + NB_PATH
            + " paths): "
            + (endTime - startTime)
            + " ms");
    // Performance note: HW approximation: 18-Aug-11: On Mac Pro 3.2 GHz Quad-Core Intel Xeon: 9200
    // ms for 1000 swaptions (12500 paths).

    final double difference =
        pvPayerLongExplicit.getAmount(EUR) - pvPayerLongIntegration.getAmount(EUR);
    final double difference2 =
        pvPayerLongExplicit.getAmount(EUR) - pvPayerLongApproximation.getAmount(EUR);
    //      double difference3 = pvPayerLongExplicit.getAmount(CUR) - pvPayerLongMC.getAmount(CUR);
    System.out.println("Difference explicit-integration: " + difference);
    System.out.println("Difference explicit-approximation: " + difference2);
    //      System.out.println("Difference explicit-Monte Carlo: " + difference3);
    System.out.println("Curve sensitivity: " + pvcs.toString());
    System.out.println("HW sensitivity: " + Arrays.toString(pvhws));
  }
  @Test
  /**
   * Tests the toDerivative method on the payment date. valuation is at noon, payment set at
   * midnight...
   */
  public void toDerivativeJustAfterPayment() {
    final ZonedDateTime referenceDate = DateUtils.getUTCDate(2011, 9, 19);
    final ZonedDateTime valuationTimeIsNoon = DateUtils.getUTCDate(2011, 9, 19, 12, 0);
    assertTrue(
        "valuationTimeIsNoon used to be after paymentDate, which was midnight. Confirm behaviour",
        valuationTimeIsNoon.isAfter(ON_COMPOUNDED_COUPON_DEFINITION.getPaymentDate()));
    final double fixingRate = 0.01;
    final DoubleTimeSeries<ZonedDateTime> fixingTS =
        ImmutableZonedDateTimeDoubleTimeSeries.ofUTC(
            new ZonedDateTime[] {
              DateUtils.getUTCDate(2011, 9, 7),
              DateUtils.getUTCDate(2011, 9, 8),
              DateUtils.getUTCDate(2011, 9, 9),
              DateUtils.getUTCDate(2011, 9, 12),
              DateUtils.getUTCDate(2011, 9, 13),
              DateUtils.getUTCDate(2011, 9, 14),
              DateUtils.getUTCDate(2011, 9, 15)
            },
            new double[] {
              fixingRate, fixingRate, fixingRate, fixingRate, fixingRate, fixingRate, fixingRate
            });
    final Payment cpnConverted =
        ON_COMPOUNDED_COUPON_DEFINITION.toDerivative(valuationTimeIsNoon, fixingTS);
    final double paymentTime =
        -TimeCalculator.getTimeBetween(
            referenceDate, EUR_PAYMENT_DATE, EUR_DAY_COUNT, EUR_CALENDAR);
    final double notionalAccrued =
        NOTIONAL
            * Math.pow(
                1 + fixingRate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodAccrualFactors()[0])
            * Math.pow(
                1 + fixingRate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodAccrualFactors()[1])
            * Math.pow(
                1 + fixingRate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodAccrualFactors()[2])
            * Math.pow(
                1 + fixingRate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodAccrualFactors()[3])
            * Math.pow(
                1 + fixingRate, ON_COMPOUNDED_COUPON_DEFINITION.getFixingPeriodAccrualFactors()[4]);
    final CouponFixed cpnExpected =
        new CouponFixed(
            EUR_CUR,
            paymentTime,
            EUR_PAYMENT_YEAR_FRACTION,
            NOTIONAL,
            (notionalAccrued / NOTIONAL - 1.0) / EUR_PAYMENT_YEAR_FRACTION);
    assertEquals("CouponONCompounded definition: toDerivative", cpnExpected, cpnConverted);

    // Test pricing, too. Notice that the value of a coupon on its payment date is non-zero
    final MulticurveProviderDiscount curves =
        MulticurveProviderDiscountDataSets.createMulticurveEurUsd();
    final MultipleCurrencyAmount pvConverted =
        com.opengamma.analytics.financial.interestrate.payments.provider
            .CouponFixedDiscountingMethod.getInstance()
            .presentValue((CouponFixed) cpnConverted, curves);
    final MultipleCurrencyAmount pvExpected =
        com.opengamma.analytics.financial.interestrate.payments.provider
            .CouponFixedDiscountingMethod.getInstance()
            .presentValue(cpnExpected, curves);
    assertEquals("CouponONCompounded definition: toDerivative", pvConverted, pvExpected);
    assertEquals(
        "CouponONCompounded definition: toDerivative",
        pvConverted,
        MultipleCurrencyAmount.of(EUR_CUR, 19744.6689499392));
  }
  /**
   * Computes the present value of an Ibor cap/floor in arrears by replication based on the paper,
   * "Swap and Cap/Floors with Fixing in Arrears or Payment Delay," OpenGamma Quantitative
   * Documentation
   * http://developers.opengamma.com/quantitative-research/In-Arrears-and-Payment-Delay-Swaps-and-Caps-OpenGamma.pdf
   *
   * @param cap The cap/floor
   * @param curves The curves
   * @return The present value
   */
  public MultipleCurrencyAmount presentValue(
      final CapFloorIbor cap, final MulticurveProviderInterface curves) {
    ArgumentChecker.notNull(cap, "The cap/floor shoud not be null");
    ArgumentChecker.notNull(curves, "curves");
    final Currency ccy = cap.getCurrency();
    // Construct a "standard" CapFloorIbor whose paymentTime is set to be fixingPeriodEndTime
    CapFloorIbor capStandard =
        new CapFloorIbor(
            cap.getCurrency(),
            cap.getFixingPeriodEndTime(),
            cap.getPaymentYearFraction(),
            cap.getNotional(),
            cap.getFixingTime(),
            cap.getIndex(),
            cap.getFixingPeriodStartTime(),
            cap.getFixingPeriodEndTime(),
            cap.getFixingAccrualFactor(),
            cap.getStrike(),
            cap.isCap());
    final double forward =
        curves.getSimplyCompoundForwardRate(
            cap.getIndex(),
            cap.getFixingPeriodStartTime(),
            cap.getFixingPeriodEndTime(),
            cap.getFixingAccrualFactor());
    final double beta =
        (1.0 + cap.getFixingAccrualFactor() * forward)
            * curves.getDiscountFactor(ccy, cap.getFixingPeriodEndTime())
            / curves.getDiscountFactor(ccy, cap.getFixingPeriodStartTime());

    final double df =
        curves.getDiscountFactor(capStandard.getCurrency(), capStandard.getPaymentTime());
    final double strikePart =
        (1.0 + cap.getFixingAccrualFactor() * capStandard.getStrike())
            * presentValueStandard(
                forward,
                capStandard.getStrike(),
                capStandard.getFixingTime(),
                capStandard.isCap(),
                df,
                capStandard.getNotional(),
                capStandard.getPaymentYearFraction());

    final InArrearsIntegrant integrant = new InArrearsIntegrant(capStandard, curves);
    double integralPart;
    try {
      if (cap.isCap()) {
        double atmVol = _smileFunction.getVolatility(forward);
        double upper = forward * Math.exp(6.0 * atmVol * Math.sqrt(cap.getFixingTime()));
        double strike = cap.getStrike();
        integralPart = INTEGRATOR.integrate(integrant, strike, upper);
        double reminder = integrant.evaluate(upper) * upper;
        double error = reminder / integralPart;

        int count = 0;
        while (Math.abs(error) > REL_ERROR && count < MAX_COUNT) {
          integralPart += INTEGRATOR.integrate(integrant, upper, 2.0 * upper);
          upper *= 2.0;
          // The increase of integralPart in the next loop is bounded by reminder
          reminder = integrant.evaluate(upper) * upper;
          error = reminder / integralPart;
          ++count;
          if (count == MAX_COUNT) {
            LOGGER.info(
                "Maximum iteration count, "
                    + MAX_COUNT
                    + ", has been reached. Relative error is greater than "
                    + REL_ERROR);
          }
        }
      } else {
        double strike = cap.getStrike();
        integralPart = INTEGRATOR.integrate(integrant, REL_TOL * strike, strike);
      }
    } catch (final Exception e) {
      throw new MathException(e);
    }
    integralPart *= 2.0 * cap.getFixingAccrualFactor();
    final double pv = (strikePart + integralPart) / beta;
    return MultipleCurrencyAmount.of(cap.getCurrency(), pv);
  }