/**
* Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.interestrate.capletstripping;
import static org.testng.AssertJUnit.assertEquals;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import org.threeten.bp.Period;
import com.opengamma.analytics.financial.instrument.index.IborIndex;
import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve;
import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount;
import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve;
import com.opengamma.analytics.math.function.Function1D;
import com.opengamma.analytics.math.interpolation.Interpolator1D;
import com.opengamma.analytics.math.interpolation.Interpolator1DFactory;
import com.opengamma.analytics.math.matrix.DoubleMatrix1D;
import com.opengamma.analytics.math.matrix.DoubleMatrix2D;
import com.opengamma.analytics.util.AssertMatrix;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConventions;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCounts;
import com.opengamma.util.ArgumentChecker;
import com.opengamma.util.money.Currency;
/**
* This contains market data for testing caplet stripping methods
*/
public abstract class CapletStrippingSetup {
private static final double TINY = 1e-20;
private static final double[] INDEX_CURVE_NODES = new double[] {0.0438356164383561, 0.0876712328767123, 0.172602739726027, 0.254794520547945, 0.506849315068493, 0.758904109589041, 1.00547945205479,
2.01369863013698, 3.01020285949547, 4.00547945205479, 5.00547945205479, 6.00547945205479, 7.01839958080694, 8.01095890410959, 9.00821917808219, 10.0082191780821, 15.0074706190583,
20.0082191780821, 25.0109589041095, 30.0136986301369 };
private static final double[] INDEX_CURVE_VALUES = new double[] {0.00184088091044285, 0.00201024117395892, 0.00241264832694067, 0.00280755413825359, 0.0029541307818572, 0.00310125437814943,
0.00320054435838637, 0.00377914611772073, 0.00483320020067661, 0.00654829256979543, 0.00877749583222556, 0.0112470678648412, 0.0136301644164456, 0.0157618031582798, 0.0176836551757772,
0.0194174141169365, 0.0254011614777518, 0.0282527762712854, 0.0298620063409043, 0.031116719228976 };
private static final double[] DIS_CURVE_NODES = new double[] {0.00273972602739726, 0.0876712328767123, 0.172602739726027, 0.254794520547945, 0.345205479452054, 0.424657534246575, 0.506849315068493,
0.758904109589041, 1.00547945205479, 2.00547945205479, 3.01020285949547, 4.00547945205479, 5.00547945205479, 10.0054794520547 };
private static final double[] DIS_CURVE_VALUES = new double[] {0.00212916045658802, 0.00144265912946933, 0.00144567477491987, 0.00135441424749791, 0.00134009103595346, 0.00132773752749976,
0.00127592397233014, 0.00132302501180961, 0.00138688847322639, 0.00172748279241698, 0.00254381216780551, 0.00410024606039574, 0.00628782387356631, 0.0170033466745807 };
private static final double[] CAP_STRIKES = new double[] {0.005, 0.01, 0.015, 0.02, 0.025, 0.03, 0.035, 0.04, 0.045, 0.05, 0.055, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12 };
private static final double[] CAP_START_TIMES = new double[] {0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25 };
private static final double[] CAP_END_TIMES = new double[] {1, 2, 3, 4, 5, 7, 10 };
// cal vol at each strike - NaN represents missing data
private static final double[][] CAP_VOLS = new double[][] { {0.7175, 0.7781, 0.8366, Double.NaN, 0.8101, 0.7633, 0.714 }, {Double.NaN, Double.NaN, 0.7523, 0.7056, 0.66095, 0.5933, 0.5313 },
{Double.NaN, 0.78086, 0.73987, 0.667, 0.61469, 0.53502, 0.4691 }, {Double.NaN, Double.NaN, Double.NaN, 0.63455, 0.56975, 0.48235, 0.4369 },
{Double.NaN, 0.80496, 0.73408, 0.6081, 0.5472, 0.46445, 0.38854 }, {Double.NaN, Double.NaN, Double.NaN, 0.61055, 0.52865, 0.432, 0.365 },
{0.96976, 0.82268, 0.73761, Double.NaN, 0.5168, 0.4183, 0.35485 }, {0.98927, 0.8376, 0.7274, 0.5983, 0.5169, 0.4083, 0.3375 },
{1.00627, 0.83618, Double.NaN, 0.6003, Double.NaN, Double.NaN, 0.34498 }, {1.02132, 0.8391, 0.7226, 0.5921, 0.4984, 0.3914, 0.32155 }, {1.0255, 0.8406, 0.7196, 0.5962, 0.5035, 0.3873, 0.3227 },
{1.0476, 0.8411, 0.7072, 0.58845, 0.50055, 0.3856, 0.3135 }, {1.0467, Double.NaN, 0.70655, 0.5855, 0.50165, 0.3824, 0.3093 }, {Double.NaN, Double.NaN, 0.70495, 0.58455, 0.499, 0.3802, 0.316 },
{Double.NaN, 0.8458, 0.70345, 0.58335, 0.4984, 0.38905, 0.3164 }, {Double.NaN, 0.8489, 0.70205, 0.58245, 0.4999, 0.39155, 0.3239 },
{Double.NaN, Double.NaN, 0.7009, 0.5824, 0.5059, 0.4005, 0.3255 }, {Double.NaN, Double.NaN, 0.6997, 0.5818, 0.5059, 0.4014, 0.3271 } };
private static final double[] CAP_ATM_VOL = new double[] {0.69025, 0.753, 0.8284, 0.7907, 0.7074, 0.53703, 0.45421 };
private static final Currency CUR = Currency.USD;
private static final Period TENOR = Period.ofMonths(3);
private static final int FREQUENCY = 4;
private static final int SETTLEMENT_DAYS = 2;
private static final DayCount DAY_COUNT_INDEX = DayCounts.ACT_360;
private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventions.MODIFIED_FOLLOWING;
private static final boolean IS_EOM = true;
private static final IborIndex INDEX = new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM, "USDLIBOR3M");
private static final MulticurveProviderDiscount YIELD_CURVES;
private static final MultiCapFloorPricer PRICER;
static {
final Interpolator1D interpolator = Interpolator1DFactory.getInterpolator(Interpolator1DFactory.DOUBLE_QUADRATIC);
final InterpolatedDoublesCurve curve1 = new InterpolatedDoublesCurve(INDEX_CURVE_NODES, INDEX_CURVE_VALUES, interpolator, true);
final InterpolatedDoublesCurve curve2 = new InterpolatedDoublesCurve(DIS_CURVE_NODES, DIS_CURVE_VALUES, interpolator, true);
// single curve for discount and projection (this is consistent with Bloomberg's cap quotes)
final YieldCurve indexCurve = YieldCurve.from(curve1);
final YieldCurve disCurve = YieldCurve.from(curve2);
// YIELD_CURVES = new YieldCurveBundle();
YIELD_CURVES = new MulticurveProviderDiscount();
YIELD_CURVES.setCurve(CUR, disCurve);
YIELD_CURVES.setCurve(INDEX, indexCurve);
PRICER = new MultiCapFloorPricer(getAllCaps(), YIELD_CURVES);
}
protected double[] getAtmVols() {
return CAP_ATM_VOL;
}
protected static MulticurveProviderDiscount getYieldCurves() {
return YIELD_CURVES;
}
static protected int getNumberOfStrikes() {
return CAP_STRIKES.length;
}
static protected double[] getStrikes() {
return CAP_STRIKES;
}
protected static double[] getCapStartTimes() {
return CAP_START_TIMES;
}
protected static double[] getCapEndTimes() {
return CAP_END_TIMES;
}
static protected IborIndex getIndex() {
return INDEX;
}
protected static double[] getCapVols(final int strikeIndex) {
final double[] vols = CAP_VOLS[strikeIndex];
final int n = vols.length;
final double[] temp = new double[n];
int ii = 0;
for (int i = 0; i < n; i++) {
if (!Double.isNaN(vols[i])) {
temp[ii++] = vols[i];
}
}
final double[] res = new double[ii];
System.arraycopy(temp, 0, res, 0, ii);
return res;
}
protected static double[] getATMCapVols() {
return CAP_ATM_VOL;
}
protected static double[] getAllCapVolsExATM() {
final int nStrikes = CAP_STRIKES.length; // number of absolute strikes
final int nTimes = CAP_END_TIMES.length;
final double[] temp = new double[nStrikes * nTimes];
int jj = 0;
for (int i = 0; i < nStrikes; i++) {
final double[] v = getCapVols(i);
final int n = v.length;
System.arraycopy(v, 0, temp, jj, n);
jj += n;
}
final double[] vols = new double[jj];
System.arraycopy(temp, 0, vols, 0, jj);
return vols;
}
protected static double[] getAllCapVols() {
final int nStrikes = CAP_STRIKES.length; // number of absolute strikes
final int nTimes = CAP_END_TIMES.length;
final double[] temp = new double[(nStrikes + 1) * nTimes];
int jj = 0;
for (int i = 0; i < nStrikes; i++) {
final double[] v = getCapVols(i);
final int n = v.length;
System.arraycopy(v, 0, temp, jj, n);
jj += n;
}
System.arraycopy(CAP_ATM_VOL, 0, temp, jj, nTimes);
jj += nTimes;
final double[] vols = new double[jj];
System.arraycopy(temp, 0, vols, 0, jj);
return vols;
}
protected static double[] getCapPrices(final int strikeIndex) {
final MultiCapFloorPricer pricer = new MultiCapFloorPricer(getCaps(strikeIndex), YIELD_CURVES);
return pricer.price(getCapVols(strikeIndex));
}
protected static double[] getATMCapPrices() {
final MultiCapFloorPricer pricer = new MultiCapFloorPricer(getATMCaps(), YIELD_CURVES);
return pricer.price(CAP_ATM_VOL);
}
protected static double[] getAllCapPrices() {
return PRICER.price(getAllCapVols());
}
protected static List<CapFloor> getCaps(final int strikeIndex) {
final double k = CAP_STRIKES[strikeIndex];
final double[] vols = CAP_VOLS[strikeIndex];
final int n = vols.length;
final double[] temp = new double[n];
int ii = 0;
for (int i = 0; i < n; i++) {
if (!Double.isNaN(vols[i])) {
temp[ii++] = CAP_END_TIMES[i];
}
}
final double[] times = new double[ii];
final double[] strikes = new double[ii];
System.arraycopy(temp, 0, times, 0, ii);
Arrays.fill(strikes, k);
return makeCaps(strikes, times);
}
/**
* Get the set of ATM caps. The strike (cap forward or swap rate) is determined from the know yield curves
* @return set of ATM caps
*/
protected static List<CapFloor> getATMCaps() {
final int n = CAP_END_TIMES.length;
final double[] dummyK = new double[n];
final List<CapFloor> dummyCaps = makeCaps(dummyK, CAP_END_TIMES);
final List<CapFloor> caps = new ArrayList<>(n);
final Iterator<CapFloor> iterator = dummyCaps.iterator();
while (iterator.hasNext()) {
final CapFloor c = iterator.next();
final CapFloorPricer pricer = new CapFloorPricer(c, YIELD_CURVES);
final double fwd = pricer.getCapForward();
caps.add(c.withStrike(fwd));
}
return caps;
}
protected static List<CapFloor> getAllCaps() {
final List<CapFloor> caps = getAllCapsExATM();
caps.addAll(getATMCaps());
return caps;
}
protected static List<CapFloor> getAllCapsExATM() {
final int nStrikes = CAP_STRIKES.length; // number of absolute strikes
final List<CapFloor> caps = new ArrayList<>((nStrikes + 1) * CAP_END_TIMES.length);
for (int i = 0; i < nStrikes; i++) {
caps.addAll(getCaps(i));
}
return caps;
}
protected static List<CapFloor> makeCaps(final double[] strikes, final double[] capEndTime) {
final int n = strikes.length;
ArgumentChecker.isTrue(n == capEndTime.length, "stikes and capEndTime different length");
final List<CapFloor> caps = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
final CapFloor cap = SimpleCapFloorMaker.makeCap(CUR, INDEX, 1, (int) (FREQUENCY * capEndTime[i]), strikes[i], true);
caps.add(cap);
}
return caps;
}
/**
* Run a caplet stripper and test it against known results
* @param stripper The caplet stripper
* @param mktValues market value of the cap/floors
* @param type Are the market values prices or volatilities
* @param expectedResults The expected result
* @param tol A machine tolerance to compare results
*/
public void testStripping(CapletStripper stripper, double[] mktValues, MarketDataType type, double expChiSqr, DoubleMatrix1D expFitParms, double tol, boolean print) {
CapletStrippingResult results = stripper.solve(mktValues, type);
if (print) {
System.out.println(this.toString() + "testStripping");
System.out.print(results);
} else {
assertEquals("chi2", expChiSqr, results.getChiSqr(), tol * expChiSqr + TINY);
AssertMatrix.assertEqualsVectors(expFitParms, results.getFitParameters(), tol);
}
}
/**
* Run a caplet stripper and test it against known results
* @param stripper The caplet stripper
* @param mktValues market value of the cap/floors
* @param type Are the market values prices or volatilities
* @param errors The errors use for a least-squares fit
* @param expectedResults The expected result
* @param tol A machine tolerance to compare results
*/
public void testStripping(CapletStripper stripper, double[] mktValues, MarketDataType type, double[] errors, double expChiSqr, DoubleMatrix1D expFitParms, double tol, boolean print) {
CapletStrippingResult results = stripper.solve(mktValues, type, errors);
if (print) {
System.out.println(this.toString() + "testStripping");
System.out.print(results);
} else {
assertEquals("chi2", expChiSqr, results.getChiSqr(), tol * expChiSqr + TINY);
AssertMatrix.assertEqualsVectors(expFitParms, results.getFitParameters(), tol);
}
}
/**
* Run a caplet stripper and test it against known results
* @param stripper The caplet stripper
* @param mktValues market value of the cap/floors
* @param type Are the market values prices or volatilities
* @param guess Starting guess of model parameters
* @param expectedResults The expected result
* @param tol A machine tolerance to compare results
*/
public void testStripping(CapletStripper stripper, double[] mktValues, MarketDataType type, DoubleMatrix1D guess, double expChiSqr, DoubleMatrix1D expFitParms, double tol, boolean print) {
CapletStrippingResult results = stripper.solve(mktValues, type, guess);
if (print) {
System.out.println(this.toString() + "testStripping");
System.out.print(results);
} else {
assertEquals("chi2", expChiSqr, results.getChiSqr(), tol * expChiSqr + TINY);
AssertMatrix.assertEqualsVectors(expFitParms, results.getFitParameters(), tol);
}
}
/**
* Run a caplet stripper and test it against known results
* @param stripper The caplet stripper
* @param mktValues market value of the cap/floors
* @param type Are the market values prices or volatilities
* @param errors The errors use for a least-squares fit
* @param guess Starting guess of model parameters
* @param expectedResults The expected result
* @param tol A machine tolerance to compare results
*/
public void testStripping(CapletStripper stripper, double[] mktValues, MarketDataType type, double[] errors, DoubleMatrix1D guess, double expChiSqr, DoubleMatrix1D expFitParms, double tol,
boolean print) {
CapletStrippingResult results = stripper.solve(mktValues, type, errors, guess);
if (print) {
System.out.println(this.toString() + "testStripping");
System.out.print(results);
} else {
assertEquals("chi2", expChiSqr, results.getChiSqr(), tol * expChiSqr + TINY);
AssertMatrix.assertEqualsVectors(expFitParms, results.getFitParameters(), tol);
}
}
protected void compareFunc(final Function1D<DoubleMatrix1D, DoubleMatrix1D> func1, final Function1D<DoubleMatrix1D, DoubleMatrix1D> func2, final DoubleMatrix1D pos, final double tol) {
final DoubleMatrix1D y1 = func1.evaluate(pos);
final DoubleMatrix1D y2 = func2.evaluate(pos);
AssertMatrix.assertEqualsVectors(y1, y2, tol);
}
protected void compareJacobianFunc(final Function1D<DoubleMatrix1D, DoubleMatrix2D> jFunc1, final Function1D<DoubleMatrix1D, DoubleMatrix2D> jFunc2, final DoubleMatrix1D pos, final double tol) {
final DoubleMatrix2D jac1 = jFunc1.evaluate(pos);
final DoubleMatrix2D jac2 = jFunc2.evaluate(pos);
AssertMatrix.assertEqualsMatrix(jac1, jac2, tol);
}
}