/** * 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 java.io.PrintStream; import java.util.HashMap; import java.util.Map; import com.opengamma.analytics.financial.model.volatility.discrete.DiscreteVolatilityFunction; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolator; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.GridInterpolator2D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle; import com.opengamma.analytics.math.matrix.DoubleMatrix1D; import com.opengamma.util.ArgumentChecker; import com.opengamma.util.tuple.DoublesPair; /** * Holds the results of performing caplet stripping on a set of caps (on the same Ibor index) */ public abstract class CapletStrippingResult { private final DoubleMatrix1D _fitParms; private final DiscreteVolatilityFunction _func; private final MultiCapFloorPricer _pricer; /** * set up the results * @param fitParms The calibrated model parameters * @param func the function that maps model parameters into caplet volatilities * @param pricer The pricer (which contained the details of the market values of the caps/floors) used in the calibrate */ public CapletStrippingResult(DoubleMatrix1D fitParms, DiscreteVolatilityFunction func, MultiCapFloorPricer pricer) { ArgumentChecker.notNull(fitParms, "fitParms"); ArgumentChecker.notNull(func, "func"); ArgumentChecker.notNull(pricer, "pricer"); _fitParms = fitParms; _func = func; _pricer = pricer; } /** * This will be zero for root-finding methods. For least-squares methods it is a (weighted) sum of squares between * the market and (calibrated) model values * @return The chi-squared */ public abstract double getChiSqr(); /** * The calibrated model parameters * @return the fit parameters */ public DoubleMatrix1D getFitParameters() { return _fitParms; } /** * The calibrated caplet volatilities * @return the caplet volatilities */ public DoubleMatrix1D getCapletVols() { return _func.evaluate(_fitParms); } /** * The calibrated cap prices * @return cap prices */ public double[] getModelCapPrices() { return _pricer.priceFromCapletVols(getCapletVols().getData()); } /** * The calibrated cap volatilities * @return cap vols */ public double[] getModelCapVols() { return _pricer.impliedVols(getModelCapPrices()); } /** * get the pricer used in the calibration * @return the pricer */ MultiCapFloorPricer getPricer() { return _pricer; } @Override public String toString() { StringBuilder builder = new StringBuilder(); builder.append("Caplet Stripping Results\nchi2:\t"); builder.append(getChiSqr()); builder.append("\nFit Parameters:"); toTabSeparated(builder, getFitParameters()); builder.append("\nCap Volatilities:"); toTabSeparated(builder, getModelCapVols()); builder.append("\nCaplet Volatilities:"); toTabSeparated(builder, getCapletVols()); builder.append("\n\n"); return builder.toString(); } /** * Dump out the caplet volatilities (order by strike then expiry) in a tab separated format (this allows easy pasting * into Excel) * @param out an output stream */ public void printCapletVols(PrintStream out) { ArgumentChecker.notNull(out, "out"); DoublesPair[] expStrikes = _pricer.getExpiryStrikeArray(); DoubleMatrix1D vols = getCapletVols(); int n = expStrikes.length; out.println("List of calibrated caplet volatilities"); out.println("Expiry\tStrike\tVolatility"); for (int i = 0; i < n; i++) { out.println(expStrikes[i].first + "\t" + expStrikes[i].second + "\t" + vols.getEntry(i)); } out.println(); } /** * Dump out the caplet volatility surface n a tab separated format (this allows easy pasting * into Excel). We store the (calibrated) caplet volatilities, rather than a continuous surface, so we create * a surface using a 2D linear grid interpolator ({@link GridInterpolator2D}. * @param out an output stream * @param nExpPoints number of sample points in the expiry direction * @param nStrikePoints number of sample points in the strike direction */ public void printSurface(PrintStream out, int nExpPoints, int nStrikePoints) { ArgumentChecker.notNull(out, "out"); ArgumentChecker.isTrue(nExpPoints > 1, "need at least 2 expiry points"); ArgumentChecker.isTrue(nExpPoints > 2, "need at least 2 strike points"); double[] t = _pricer.getCapletExpiries(); double[] k = _pricer.getStrikes(); double timeRange = t[t.length - 1] - t[0]; double strikeRange = k[k.length - 1] - k[0]; DoublesPair[] expStrikes = _pricer.getExpiryStrikeArray(); DoubleMatrix1D vols = getCapletVols(); int n = expStrikes.length; Map<DoublesPair, Double> map = new HashMap<>(n); for (int i = 0; i < n; i++) { map.put(expStrikes[i], vols.getEntry(i)); } CombinedInterpolatorExtrapolator interpolator = CombinedInterpolatorExtrapolatorFactory.getInterpolator(Interpolator1DFactory.LINEAR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); GridInterpolator2D interpolator2D = new GridInterpolator2D(interpolator, interpolator); Map<Double, Interpolator1DDataBundle> db = interpolator2D.getDataBundle(map); double[] times = new double[nExpPoints]; double[] strikes = new double[nStrikePoints]; for (int i = 0; i < nStrikePoints; i++) { strikes[i] = k[0] + strikeRange * i / (nStrikePoints - 1.0); } out.println(); for (int j = 0; j < nExpPoints; j++) { times[j] = t[0] + timeRange * j / (nExpPoints - 1.0); out.print("\t" + times[j]); } for (int i = 0; i < nStrikePoints; i++) { out.print("\n" + strikes[i]); for (int j = 0; j < nExpPoints; j++) { Double vol = interpolator2D.interpolate(db, DoublesPair.of(times[j], strikes[i])); out.print("\t" + vol); } } out.println(); } private void toTabSeparated(StringBuilder builder, DoubleMatrix1D data) { toTabSeparated(builder, data.getData()); } private void toTabSeparated(StringBuilder builder, double[] data) { int n = data.length; for (int i = 0; i < n; i++) { builder.append("\t"); builder.append(data[i]); } } }