/**
* Copyright (C) 2015 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.impl.volatility.smile;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.util.BitSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.math.impl.minimization.NonLinearParameterTransforms;
import com.opengamma.strata.math.impl.statistics.leastsquare.LeastSquareResultsWithTransform;
import cern.jet.random.engine.MersenneTwister;
import cern.jet.random.engine.RandomEngine;
/**
* Test {@link SabrModelFitter}.
*/
@Test
public class SabrModelFitterTest extends SmileModelFitterTest<SabrFormulaData> {
private static double ALPHA = 0.05;
private static double BETA = 0.5;
private static double RHO = -0.3;
private static double NU = 0.2;
private static Logger log = LoggerFactory.getLogger(SabrModelFitterTest.class);
private static RandomEngine RANDOM = new MersenneTwister();
SabrModelFitterTest() {
_chiSqEps = 1e-10;
}
@Override
VolatilityFunctionProvider<SabrFormulaData> getModel() {
return SabrHaganVolatilityFunctionProvider.DEFAULT;
}
@Override
SabrFormulaData getModelData() {
return SabrFormulaData.of(ALPHA, BETA, RHO, NU);
}
@Override
SmileModelFitter<SabrFormulaData> getFitter(double forward, double[] strikes, double timeToExpiry,
double[] impliedVols, double[] error, VolatilityFunctionProvider<SabrFormulaData> model) {
return new SabrModelFitter(forward, DoubleArray.copyOf(strikes), timeToExpiry, DoubleArray.copyOf(impliedVols),
DoubleArray.copyOf(error), model);
}
@Override
double[][] getStartValues() {
return new double[][] {{0.1, 0.7, 0.0, 0.3}, {0.01, 0.95, 0.9, 0.4}, {0.01, 0.5, -0.7, 0.6}};
}
@Override
Logger getlogger() {
return log;
}
@Override
BitSet[] getFixedValues() {
BitSet[] fixed = new BitSet[3];
fixed[0] = new BitSet();
fixed[1] = new BitSet();
fixed[2] = new BitSet();
fixed[2].set(1);
return fixed;
}
@Override
double[] getRandomStartValues() {
double alpha = 0.1 + 0.4 * RANDOM.nextDouble();
double beta = RANDOM.nextDouble();
double rho = 2 * RANDOM.nextDouble() - 1;
double nu = 1.5 * RANDOM.nextDouble();
return new double[] {alpha, beta, rho, nu};
}
public void testExactFitOddStart() {
double[] start = new double[] {0.01, 0.99, 0.9, 0.4};
LeastSquareResultsWithTransform results = _fitter.solve(DoubleArray.copyOf(start));
double[] res = results.getModelParameters().toArray();
double eps = 1e-6;
assertEquals(ALPHA, res[0], eps);
assertEquals(BETA, res[1], eps);
assertEquals(RHO, res[2], eps);
assertEquals(NU, res[3], eps);
assertEquals(0.0, results.getChiSq(), eps);
}
public void testExactFitWithTransform() {
double[] start = new double[] {0.01, 0.99, 0.9, 0.4};
NonLinearParameterTransforms transf = _fitter.getTransform(DoubleArray.copyOf(start));
LeastSquareResultsWithTransform results = _fitter.solve(DoubleArray.copyOf(start), transf);
double[] res = results.getModelParameters().toArray();
double eps = 1e-6;
assertEquals(ALPHA, res[0], eps);
assertEquals(BETA, res[1], eps);
assertEquals(RHO, res[2], eps);
assertEquals(NU, res[3], eps);
assertEquals(0.0, results.getChiSq(), eps);
}
public void testExactFitWithFixedBeta() {
DoubleArray start = DoubleArray.of(0.1, 0.5, 0.0, 0.3);
BitSet fixed = new BitSet();
fixed.set(1);
LeastSquareResultsWithTransform results = _fitter.solve(start, fixed);
double[] res = results.getModelParameters().toArray();
double eps = 1e-6;
assertEquals(ALPHA, res[0], eps);
assertEquals(BETA, res[1], eps);
assertEquals(RHO, res[2], eps);
assertEquals(NU, res[3], eps);
assertEquals(0.0, results.getChiSq(), eps);
// sensitivity to data
DoubleMatrix sensitivity = results.getModelParameterSensitivityToData();
double shiftFd = 1.0E-5;
for (int i = 0; i < _cleanVols.length; i++) {
double[] volBumpedP = _cleanVols.clone();
volBumpedP[i] += shiftFd;
SabrModelFitter fitterP = new SabrModelFitter(F, DoubleArray.copyOf(STRIKES), TIME_TO_EXPIRY,
DoubleArray.copyOf(volBumpedP), DoubleArray.copyOf(_errors), getModel());
LeastSquareResultsWithTransform resultsBumpedP = fitterP.solve(start, fixed);
DoubleArray parameterBumpedP = resultsBumpedP.getModelParameters();
double[] volBumpedM = _cleanVols.clone();
volBumpedM[i] -= shiftFd;
SabrModelFitter fitterM = new SabrModelFitter(F, DoubleArray.copyOf(STRIKES), TIME_TO_EXPIRY,
DoubleArray.copyOf(volBumpedM), DoubleArray.copyOf(_errors), getModel());
LeastSquareResultsWithTransform resultsBumpedM = fitterM.solve(start, fixed);
DoubleArray parameterBumpedM = resultsBumpedM.getModelParameters();
DoubleArray sensitivityColumnFd = parameterBumpedP.minus(parameterBumpedM).dividedBy(2 * shiftFd);
assertTrue(sensitivityColumnFd.equalWithTolerance(sensitivity.column(i), 1.0E-6));
}
}
public void testNoisyFitWithFixedBeta() {
DoubleArray start = DoubleArray.of(0.1, 0.5, 0.0, 0.3);
BitSet fixed = new BitSet();
fixed.set(1);
LeastSquareResultsWithTransform results = _nosiyFitter.solve(start, fixed);
double[] res = results.getModelParameters().toArray();
double eps = 1e-2;
assertEquals(ALPHA, res[0], eps);
assertEquals(BETA, res[1], eps);
assertEquals(RHO, res[2], eps);
assertEquals(NU, res[3], eps);
assertEquals(0.0, results.getChiSq(), 10.0d);
// sensitivity to data
DoubleMatrix sensitivity = results.getModelParameterSensitivityToData();
double shiftFd = 1.0E-5;
for (int i = 0; i < _cleanVols.length; i++) {
double[] volBumpedP = _noisyVols.clone();
volBumpedP[i] += shiftFd;
SabrModelFitter fitterP = new SabrModelFitter(F, DoubleArray.copyOf(STRIKES), TIME_TO_EXPIRY,
DoubleArray.copyOf(volBumpedP), DoubleArray.copyOf(_errors), getModel());
LeastSquareResultsWithTransform resultsBumpedP = fitterP.solve(start, fixed);
DoubleArray parameterBumpedP = resultsBumpedP.getModelParameters();
double[] volBumpedM = _noisyVols.clone();
volBumpedM[i] -= shiftFd;
SabrModelFitter fitterM = new SabrModelFitter(F, DoubleArray.copyOf(STRIKES), TIME_TO_EXPIRY,
DoubleArray.copyOf(volBumpedM), DoubleArray.copyOf(_errors), getModel());
LeastSquareResultsWithTransform resultsBumpedM = fitterM.solve(start, fixed);
DoubleArray parameterBumpedM = resultsBumpedM.getModelParameters();
DoubleArray sensitivityColumnFd = parameterBumpedP.minus(parameterBumpedM).dividedBy(2 * shiftFd);
assertTrue(sensitivityColumnFd.equalWithTolerance(sensitivity.column(i), 1.0E-2));
}
}
}