/**
* Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.strata.pricer.swaption;
import static org.testng.Assert.assertTrue;
import java.time.Period;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import com.opengamma.strata.basics.date.Tenor;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.market.ValueType;
import com.opengamma.strata.pricer.option.RawOptionData;
import com.opengamma.strata.pricer.option.TenorRawOptionData;
/**
* Utilities for the different tests related to {@link SabrSwaptionCalibrator}
*/
public class SabrSwaptionCalibratorSmileTestUtils {
/**
* Create a {@link RawOptionData} object for calibration from data.
*
* @param tenors the list of tenors
* @param expiries the list of expiries
* @param strikeLikeType the type of the strike-like dimension
* @param strikeLikeData the data related to the strike-like dimension
* @param dataType the type of the data
* @param dataArray the array with the raw data, including potential Double.NaN for missing data.
* @return the raw option data object
*/
public static TenorRawOptionData rawData(
List<Tenor> tenors,
List<Period> expiries,
ValueType strikeLikeType,
DoubleArray strikeLikeData,
ValueType dataType,
double[][][] dataArray) {
Map<Tenor, RawOptionData> raw = new TreeMap<>();
for (int looptenor = 0; looptenor < dataArray.length; looptenor++) {
DoubleMatrix matrix = DoubleMatrix.ofUnsafe(dataArray[looptenor]);
raw.put(tenors.get(looptenor), RawOptionData.of(expiries, strikeLikeData, strikeLikeType, matrix, dataType));
}
return TenorRawOptionData.of(raw);
}
/**
* Create a {@link RawOptionData} object for calibration from data and shift one point.
*
* @param tenors the list of tenors
* @param expiries the list of expiries
* @param strikeLikeType the type of the strike-like dimension
* @param strikeLikeData the data related to the strike-like dimension
* @param dataType the type of the data
* @param dataArray the array with the raw data, including potential Double.NaN for missing data.
* @param i the index of the tenor to shift
* @param j the index of the expiry to shift
* @param k the index of the strike-like dimension to shift
* @param shift the size of the shift
* @return the raw option data object
*/
public static TenorRawOptionData rawDataShiftPoint(
List<Tenor> tenors,
List<Period> expiries,
ValueType strikeLikeType,
DoubleArray strikeLikeData,
ValueType dataType,
double[][][] dataArray,
int i,
int j,
int k,
double shift) {
Map<Tenor, RawOptionData> raw = new TreeMap<>();
for (int looptenor = 0; looptenor < dataArray.length; looptenor++) {
double[][] shiftedData = Arrays.stream(dataArray[looptenor])
.map(row -> row.clone())
.toArray(l -> new double[l][]); // deep copy of 2d array
if (looptenor == i) {
shiftedData[j][k] += shift;
}
DoubleMatrix matrix = DoubleMatrix.ofUnsafe(shiftedData);
raw.put(tenors.get(looptenor), RawOptionData.of(expiries, strikeLikeData, strikeLikeType, matrix, dataType));
}
return TenorRawOptionData.of(raw);
}
/**
* Create a {@link RawOptionData} object for calibration from data and shift one smile.
*
* @param tenors the list of tenors
* @param expiries the list of expiries
* @param strikeLikeType the type of the strike-like dimension
* @param strikeLikeData the data related to the strike-like dimension
* @param dataType the type of the data
* @param dataArray the array with the raw data, including potential Double.NaN for missing data.
* @param i the index of the tenor to shift
* @param j the index of the expiry to shift
* @param shift the size of the shift
* @return the raw option data object
*/
public static TenorRawOptionData rawDataShiftSmile(
List<Tenor> tenors,
List<Period> expiries,
ValueType strikeLikeType,
DoubleArray strikeLikeData,
ValueType dataType,
double[][][] dataArray,
int i,
int j,
double shift) {
Map<Tenor, RawOptionData> raw = new TreeMap<>();
for (int looptenor = 0; looptenor < dataArray.length; looptenor++) {
double[][] shiftedData = Arrays.stream(dataArray[looptenor])
.map(row -> row.clone())
.toArray(l -> new double[l][]); // deep copy of 2d array
if (looptenor == i) {
for (int k = 0; k < shiftedData[j].length; k++) {
shiftedData[j][k] += shift;
}
}
DoubleMatrix matrix = DoubleMatrix.ofUnsafe(shiftedData);
raw.put(tenors.get(looptenor), RawOptionData.of(expiries, strikeLikeData, strikeLikeType, matrix, dataType));
}
return TenorRawOptionData.of(raw);
}
/**
* Check that the results are acceptable by checking that the absolute difference or the relative difference
* is below a given threshold.
*
* @param value1 the first value
* @param value2 the second value to compare
* @param tolerance the tolerance
* @param msg the message to return in case of failure
*/
public static void checkAcceptable(double value1, double value2, double tolerance, String msg) {
assertTrue((Math.abs(value1 - value2) < tolerance) ||
(Math.abs((value1 - value2) / value2) < tolerance),
msg);
}
}