/*
 * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.examples.blog.multicurve1;

import static com.opengamma.strata.product.swap.type.FixedIborSwapConventions.GBP_FIXED_6M_LIBOR_6M;

import java.time.LocalDate;
import java.time.Period;
import java.util.Arrays;
import java.util.Map;

import com.opengamma.strata.basics.ReferenceData;
import com.opengamma.strata.basics.currency.MultiCurrencyAmount;
import com.opengamma.strata.basics.date.Tenor;
import com.opengamma.strata.collect.io.ResourceLocator;
import com.opengamma.strata.collect.tuple.Pair;
import com.opengamma.strata.data.ImmutableMarketData;
import com.opengamma.strata.loader.csv.QuotesCsvLoader;
import com.opengamma.strata.loader.csv.RatesCalibrationCsvLoader;
import com.opengamma.strata.market.curve.CurveGroupName;
import com.opengamma.strata.market.curve.RatesCurveGroupDefinition;
import com.opengamma.strata.market.observable.QuoteId;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.pricer.curve.CalibrationMeasures;
import com.opengamma.strata.pricer.curve.RatesCurveCalibrator;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.sensitivity.MarketQuoteSensitivityCalculator;
import com.opengamma.strata.pricer.swap.DiscountingSwapTradePricer;
import com.opengamma.strata.product.common.BuySell;
import com.opengamma.strata.product.swap.ResolvedSwapTrade;

/**
 * Calibrates one set of curve, computes sensitivity (Bucketed PV01) and estimate computation time.
 * <p>
 * Code used for the blog "Strata and multi-curve - Blog 1: Curve calibration and bucketed PV01" available at
 * https://opengamma.com/blog/strata-and-multi-curve-curve-calibration-and-bucketed-pv01
 */
public class CalibrationPVPerformanceExample {

  /* Reference data contains calendar. Here we use build-in holiday calendar. 
   * It is possible to override them with customized versions.*/
  private static final ReferenceData REF_DATA = ReferenceData.standard();

  private static final LocalDate VALUATION_DATE = LocalDate.of(2016, 8, 1);

  // Configuration with discounting curve using OIS up to final maturity; Libor forward curve using IRS.
  private static final String CONFIG_STR = "GBP-DSCONOIS-L6MIRS";
  private static final CurveGroupName CONFIG_NAME = CurveGroupName.of(CONFIG_STR);

  /* Swap description. */
  private static final Period SWAP_PERIOD_TO_START = Period.ofMonths(3);
  private static final double SWAP_COUPON = 0.0250;
  private static final double SWAP_NOTIONAL = 10_000_000;

  /* Path to files */
  private static final String PATH_CONFIG = "src/main/resources/example-calibration/curves/";
  private static final String PATH_QUOTES = "src/main/resources/example-calibration/quotes/";
  /* Files utilities */
  private static final String SUFFIX_CSV = ".csv";
  private static final String GROUPS_SUFFIX = "-group";
  private static final String NODES_SUFFIX = "-nodes";
  private static final String SETTINGS_SUFFIX = "-settings";

  private static final ResourceLocator GROUP_RESOURCE =
      ResourceLocator.of(PATH_CONFIG + CONFIG_STR + "/" + CONFIG_STR + GROUPS_SUFFIX + SUFFIX_CSV);
  private static final ResourceLocator SETTINGS_RESOURCE =
      ResourceLocator.of(PATH_CONFIG + CONFIG_STR + "/" + CONFIG_STR + SETTINGS_SUFFIX + SUFFIX_CSV);
  private static final ResourceLocator NODES_RESOURCE =
      ResourceLocator.of(PATH_CONFIG + CONFIG_STR + "/" + CONFIG_STR + NODES_SUFFIX + SUFFIX_CSV);

  /* Raw data */
  private static final String QUOTES_FILE = PATH_QUOTES + "MARKET-QUOTES-GBP-20160801.csv";
  private static final Map<QuoteId, Double> MAP_MQ = QuotesCsvLoader.load(VALUATION_DATE, ResourceLocator.of(QUOTES_FILE));
  private static final ImmutableMarketData MARKET_QUOTES =
      ImmutableMarketData.builder(VALUATION_DATE).values(MAP_MQ).build();

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

  private static final DiscountingSwapTradePricer PRICER_SWAP = DiscountingSwapTradePricer.DEFAULT;
  private static final MarketQuoteSensitivityCalculator MQC = MarketQuoteSensitivityCalculator.DEFAULT;

  private static final int NB_COUPONS = 100;
  private static final double SWAP_COUPON_RANGE = 0.0100;
  private static final int NB_TENORS = 20;
  private static final int TENOR_START = 1;

  public static void main(String[] arg) {

    int nbRrpWarm = 2;
    int nbRunPerf = 2;

    /* Load the curve configurations from csv files */
    Map<CurveGroupName, RatesCurveGroupDefinition> configs =
        RatesCalibrationCsvLoader.load(GROUP_RESOURCE, SETTINGS_RESOURCE, NODES_RESOURCE);

    /* Construct a swaps */
    ResolvedSwapTrade[] swaps = new ResolvedSwapTrade[NB_COUPONS * NB_TENORS];
    for (int loopswap = 0; loopswap < NB_COUPONS; loopswap++) {
      for (int looptenor = 0; looptenor < NB_TENORS; looptenor++) {
        double coupon = SWAP_COUPON + loopswap * SWAP_COUPON_RANGE / NB_COUPONS;
        swaps[looptenor * NB_COUPONS + loopswap] = GBP_FIXED_6M_LIBOR_6M.createTrade(
            VALUATION_DATE, SWAP_PERIOD_TO_START, Tenor.of(Period.ofYears(TENOR_START + looptenor)),
            BuySell.BUY, SWAP_NOTIONAL, coupon, REF_DATA).resolve(REF_DATA);
      }
    }

    /* Warm-up */
    Pair<MultiCurrencyAmount[], CurrencyParameterSensitivities[]> r =
        Pair.of(new MultiCurrencyAmount[0], new CurrencyParameterSensitivities[0]);
    for (int i = 0; i < nbRrpWarm; i++) {
      r = computation(configs, swaps);
    }

    long start, end;
    start = System.currentTimeMillis();
    for (int i = 0; i < nbRunPerf; i++) {
      r = computation(configs, swaps);
    }

    end = System.currentTimeMillis();
    System.out.println("Computation time: " + (end - start) + " ms");

    System.out.println("Performance estimate for curve calibration, " + (NB_COUPONS * NB_TENORS) + " trades and " +
        nbRunPerf + " repetitions.\n" + Arrays.toString(r.getFirst()) + Arrays.toString(r.getSecond()));
  }

  private static Pair<MultiCurrencyAmount[], CurrencyParameterSensitivities[]> computation(
      Map<CurveGroupName, RatesCurveGroupDefinition> configs,
      ResolvedSwapTrade[] swaps) {

    int nbSwaps = swaps.length;

    /* Calibrate curves */
    ImmutableRatesProvider multicurve = CALIBRATOR.calibrate(configs.get(CONFIG_NAME), MARKET_QUOTES, REF_DATA);

    /* Computes PV and bucketed PV01 */
    MultiCurrencyAmount[] pv = new MultiCurrencyAmount[nbSwaps];
    CurrencyParameterSensitivities[] mqs = new CurrencyParameterSensitivities[nbSwaps];
    for (int loopswap = 0; loopswap < nbSwaps; loopswap++) {
      pv[loopswap] = PRICER_SWAP.presentValue(swaps[loopswap], multicurve);
      PointSensitivities pts = PRICER_SWAP.presentValueSensitivity(swaps[loopswap], multicurve);
      CurrencyParameterSensitivities ps = multicurve.parameterSensitivity(pts);
      mqs[loopswap] = MQC.sensitivity(ps, multicurve);
    }

    return Pair.of(pv, mqs);

  }

}
