/**
   * Computes the fair value strike of a spot starting VarianceSwap parameterized in 'variance'
   * terms, It is quoted as an annual variance value, hence 1/T * integral(0,T) {sigmaSquared dt}
   *
   * <p>
   *
   * @param expiry Time from spot until last observation
   * @param market VarianceSwapDataBundle containing volatility surface, forward underlying, and
   *     funding curve
   * @param cutoff The cutoff
   * @return presentValue of the *remaining* variance in the swap.
   */
  protected double impliedVarianceFromSpot(
      final double expiry, final VarianceSwapDataBundle market, final DoublesPair cutoff) {
    // 1. Unpack Market data
    final double fwd = market.getForwardCurve().getForward(expiry);
    final BlackVolatilitySurface<?> volSurf = market.getVolatilitySurface();

    VarianceCalculator varCal;
    if (cutoff == null) {
      varCal = new VarianceCalculator(fwd, expiry);
    } else {
      final ExtrapolationParameters exParCal = new ExtrapolationParameters(fwd, expiry);
      final Pair<double[], double[]> pars = exParCal.getparameters(volSurf, cutoff);
      final double[] ks = pars.getFirst();
      final double[] vols = pars.getSecond();
      final double res = getResidual(fwd, expiry, ks, vols);

      varCal = new VarianceCalculator(fwd, expiry, res, ks[0]);
    }

    return varCal.getVariance(volSurf);
  }
  public double presentValue(
      final VarianceSwap deriv, final VarianceSwapDataBundle market, final DoublesPair cutoff) {
    Validate.notNull(deriv, "VarianceSwap deriv");
    Validate.notNull(market, "VarianceSwapDataBundle market");

    if (deriv.getTimeToSettlement() < 0) {
      return 0.0; // All payments have been settled
    }

    // Compute contribution from past realizations
    final double realizedVar =
        new RealizedVariance().evaluate(deriv); // Realized variance of log returns already observed
    // Compute contribution from future realizations
    final double remainingVar =
        impliedVariance(deriv, market, cutoff); // Remaining variance implied by option prices

    // Compute weighting
    final double nObsExpected = deriv.getObsExpected(); // Expected number as of trade inception
    final double nObsDisrupted =
        deriv.getObsDisrupted(); // Number of observations missed due to market disruption
    double nObsActual = 0;

    if (deriv.getTimeToObsStart() <= 0) {
      Validate.isTrue(
          deriv.getObservations().length > 0,
          "presentValue requested after first observation date, yet no observations have been provided.");
      nObsActual = deriv.getObservations().length - 1; // From observation start until valuation
    }

    final double totalVar =
        realizedVar * (nObsActual / nObsExpected)
            + remainingVar * (nObsExpected - nObsActual - nObsDisrupted) / nObsExpected;
    final double finalPayment = deriv.getVarNotional() * (totalVar - deriv.getVarStrike());

    final double df = market.getDiscountCurve().getDiscountFactor(deriv.getTimeToSettlement());
    return df * finalPayment;
  }