package com.opengamma.strata.pricer.fxopt;
import static com.opengamma.strata.basics.currency.Currency.EUR;
import static com.opengamma.strata.basics.currency.Currency.USD;
import static com.opengamma.strata.basics.date.DayCounts.ACT_ACT_ISDA;
import static com.opengamma.strata.product.common.LongShort.LONG;
import static com.opengamma.strata.product.common.LongShort.SHORT;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.function.Function;
import org.testng.annotations.Test;
import com.opengamma.strata.basics.currency.CurrencyAmount;
import com.opengamma.strata.basics.currency.CurrencyPair;
import com.opengamma.strata.basics.currency.FxRate;
import com.opengamma.strata.basics.currency.MultiCurrencyAmount;
import com.opengamma.strata.basics.currency.Payment;
import com.opengamma.strata.collect.DoubleArrayMath;
import com.opengamma.strata.collect.array.DoubleArray;
import com.opengamma.strata.collect.array.DoubleMatrix;
import com.opengamma.strata.market.curve.CurveName;
import com.opengamma.strata.market.curve.interpolator.CurveExtrapolator;
import com.opengamma.strata.market.curve.interpolator.CurveExtrapolators;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolator;
import com.opengamma.strata.market.curve.interpolator.CurveInterpolators;
import com.opengamma.strata.market.param.CurrencyParameterSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivities;
import com.opengamma.strata.market.sensitivity.PointSensitivityBuilder;
import com.opengamma.strata.math.impl.linearalgebra.DecompositionResult;
import com.opengamma.strata.math.impl.linearalgebra.SVDecompositionCommons;
import com.opengamma.strata.pricer.fx.DiscountingFxSingleProductPricer;
import com.opengamma.strata.pricer.fx.RatesProviderFxDataSets;
import com.opengamma.strata.pricer.impl.option.BlackFormulaRepository;
import com.opengamma.strata.pricer.rate.ImmutableRatesProvider;
import com.opengamma.strata.pricer.sensitivity.RatesFiniteDifferenceSensitivityCalculator;
import com.opengamma.strata.product.fx.ResolvedFxSingle;
import com.opengamma.strata.product.fxopt.ResolvedFxVanillaOption;
import com.opengamma.strata.product.fxopt.ResolvedFxVanillaOptionTrade;
/**
* Test {@link VannaVolgaFxVanillaOptionProductPricer}.
*/
@Test
public class VannaVolgaFxVanillaOptionProductPricerTest {
private static final ZoneId ZONE = ZoneId.of("Z");
private static final ZonedDateTime VAL_DATETIME = ZonedDateTime.of(2011, 6, 13, 13, 10, 0, 0, ZONE);
private static final LocalDate VAL_DATE = VAL_DATETIME.toLocalDate();
private static final ZonedDateTime EXPIRY = ZonedDateTime.of(2012, 12, 13, 10, 0, 0, 0, ZONE);
private static final LocalDate PAY = LocalDate.of(2012, 12, 17);
private static final ZonedDateTime AFTER = ZonedDateTime.of(2012, 12, 24, 10, 0, 0, 0, ZONE);
private static final ImmutableRatesProvider RATES_PROVIDER =
RatesProviderFxDataSets.createProviderEurUsdActActIsda(VAL_DATE);
private static final ImmutableRatesProvider RATES_PROVIDER_AFTER =
RatesProviderFxDataSets.createProviderEurUsdActActIsda(AFTER.toLocalDate());
private static final DoubleArray TIME_TO_EXPIRY = DoubleArray.of(0.0001, 0.25205479452054796, 0.5013698630136987,
1.0015120892282356, 2.0, 5.001512089228235);
private static final DoubleArray ATM = DoubleArray.of(0.11, 0.115, 0.12, 0.12, 0.125, 0.13);
private static final DoubleArray DELTA = DoubleArray.of(0.25);
private static final DoubleMatrix RISK_REVERSAL = DoubleMatrix.ofUnsafe(new double[][] {
{0.015 }, {0.020 }, {0.025 }, {0.03 }, {0.025 }, {0.030 } });
private static final DoubleMatrix STRANGLE = DoubleMatrix.ofUnsafe(new double[][] {
{0.002 }, {0.003 }, {0.004 }, {0.0045 }, {0.0045 }, {0.0045 } });
private static final CurveInterpolator INTERP_STRIKE = CurveInterpolators.DOUBLE_QUADRATIC;
private static final CurveExtrapolator EXTRAP_STRIKE = CurveExtrapolators.LINEAR;
private static final InterpolatedStrikeSmileDeltaTermStructure SMILE_TERM =
InterpolatedStrikeSmileDeltaTermStructure.of(
TIME_TO_EXPIRY, DELTA, ATM, RISK_REVERSAL, STRANGLE, ACT_ACT_ISDA, INTERP_STRIKE, EXTRAP_STRIKE, EXTRAP_STRIKE);
private static final CurrencyPair CURRENCY_PAIR = CurrencyPair.of(EUR, USD);
private static final BlackFxOptionSmileVolatilities VOLS =
BlackFxOptionSmileVolatilities.of(FxOptionVolatilitiesName.of("Test"), CURRENCY_PAIR, VAL_DATETIME, SMILE_TERM);
private static final BlackFxOptionSmileVolatilities VOLS_AFTER =
BlackFxOptionSmileVolatilities.of(FxOptionVolatilitiesName.of("Test"), CURRENCY_PAIR, AFTER, SMILE_TERM);
private static final int NB_STRIKES = 11;
private static final double STRIKE_MIN = 1.00;
private static final double STRIKE_RANGE = 0.80;
private static final double NOTIONAL = 100_000_000;
private static final ResolvedFxVanillaOption[] CALLS = new ResolvedFxVanillaOption[NB_STRIKES];
private static final ResolvedFxVanillaOption[] PUTS = new ResolvedFxVanillaOption[NB_STRIKES];
private static final ResolvedFxSingle[] UNDERLYING = new ResolvedFxSingle[NB_STRIKES];
static {
for (int i = 0; i < NB_STRIKES; ++i) {
double strike = STRIKE_MIN + i * STRIKE_RANGE / (NB_STRIKES - 1d);
CurrencyAmount eurAmount = CurrencyAmount.of(EUR, NOTIONAL);
CurrencyAmount usdAmount = CurrencyAmount.of(USD, -NOTIONAL * strike);
UNDERLYING[i] = ResolvedFxSingle.of(eurAmount, usdAmount, PAY);
CALLS[i] = ResolvedFxVanillaOption.builder()
.longShort(LONG)
.expiry(EXPIRY)
.underlying(UNDERLYING[i])
.build();
PUTS[i] = ResolvedFxVanillaOption.builder()
.longShort(SHORT)
.expiry(EXPIRY)
.underlying(UNDERLYING[i].inverse())
.build();
}
}
private static final double TOL = 1.0e-13;
private static final double FD_EPS = 1.0e-7;
private static final VannaVolgaFxVanillaOptionProductPricer PRICER = VannaVolgaFxVanillaOptionProductPricer.DEFAULT;
private static final VannaVolgaFxVanillaOptionTradePricer TRADE_PRICER = VannaVolgaFxVanillaOptionTradePricer.DEFAULT;
private static final DiscountingFxSingleProductPricer FX_PRICER = DiscountingFxSingleProductPricer.DEFAULT;
private static final RatesFiniteDifferenceSensitivityCalculator FD_CAL =
new RatesFiniteDifferenceSensitivityCalculator(FD_EPS);
private static final SVDecompositionCommons SVD = new SVDecompositionCommons();
//-------------------------------------------------------------------------
public void test_price_presentValue() {
for (int i = 0; i < NB_STRIKES; ++i) {
ResolvedFxVanillaOption call = CALLS[i];
ResolvedFxVanillaOptionTrade callTrade = ResolvedFxVanillaOptionTrade.builder()
.product(call)
.premium(Payment.of(EUR, 0, VAL_DATE))
.build();
double computedPriceCall = PRICER.price(call, RATES_PROVIDER, VOLS);
CurrencyAmount computedCall = PRICER.presentValue(call, RATES_PROVIDER, VOLS);
double timeToExpiry = VOLS.relativeTime(EXPIRY);
FxRate forward = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER);
double forwardRate = forward.fxRate(CURRENCY_PAIR);
double strikeRate = call.getStrike();
SmileDeltaParameters smileAtTime = VOLS.getSmile().smileForExpiry(timeToExpiry);
double[] strikes = smileAtTime.strike(forwardRate).toArray();
double[] vols = smileAtTime.getVolatility().toArray();
double df = RATES_PROVIDER.discountFactor(USD, PAY);
double[] weights = weights(forwardRate, strikeRate, strikes, timeToExpiry, vols[1]);
double expectedPriceCall = BlackFormulaRepository.price(forwardRate, strikeRate, timeToExpiry, vols[1], true);
for (int j = 0; j < 3; ++j) {
expectedPriceCall += weights[j] * (
BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[j], true)
- BlackFormulaRepository.price(forwardRate, strikes[j], timeToExpiry, vols[1], true));
}
expectedPriceCall *= df;
assertEquals(computedPriceCall, expectedPriceCall, TOL);
assertEquals(computedCall.getAmount(), expectedPriceCall * NOTIONAL, TOL * NOTIONAL);
// test against trade pricer
assertEquals(computedCall, TRADE_PRICER.presentValue(callTrade, RATES_PROVIDER, VOLS).getAmount(USD));
}
}
public void test_price_presentValue_afterExpiry() {
for (int i = 0; i < NB_STRIKES; ++i) {
ResolvedFxVanillaOption call = CALLS[i];
ResolvedFxVanillaOptionTrade callTrade = ResolvedFxVanillaOptionTrade.builder()
.product(call)
.premium(Payment.of(EUR, 0, VOLS_AFTER.getValuationDate()))
.build();
double computedPriceCall = PRICER.price(call, RATES_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedCall = PRICER.presentValue(call, RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPriceCall, 0d, TOL);
assertEquals(computedCall.getAmount(), 0d, TOL);
ResolvedFxVanillaOption put = PUTS[i];
ResolvedFxVanillaOptionTrade putTrade = ResolvedFxVanillaOptionTrade.builder()
.product(put)
.premium(Payment.of(EUR, 0, VOLS_AFTER.getValuationDate()))
.build();
double computedPricePut = PRICER.price(put, RATES_PROVIDER_AFTER, VOLS_AFTER);
CurrencyAmount computedPut = PRICER.presentValue(put, RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPricePut, 0d, TOL);
assertEquals(computedPut.getAmount(), 0d, TOL);
// test against trade pricer
assertEquals(computedCall, TRADE_PRICER.presentValue(callTrade, RATES_PROVIDER_AFTER, VOLS_AFTER).getAmount(USD));
assertEquals(computedPut, TRADE_PRICER.presentValue(putTrade, RATES_PROVIDER_AFTER, VOLS_AFTER).getAmount(USD));
}
}
//-------------------------------------------------------------------------
public void test_presentValueSensitivity() {
for (int i = 0; i < NB_STRIKES; ++i) {
ResolvedFxVanillaOption option = CALLS[i];
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(option, RATES_PROVIDER, VOLS);
CurrencyParameterSensitivities sensiComputed = RATES_PROVIDER.parameterSensitivity(point.build());
double timeToExpiry = VOLS.relativeTime(EXPIRY);
double forwardRate = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER).fxRate(CURRENCY_PAIR);
double strikeRate = option.getStrike();
SmileDeltaParameters smileAtTime = VOLS.getSmile().smileForExpiry(timeToExpiry);
double[] vols = smileAtTime.getVolatility().toArray();
double df = RATES_PROVIDER.discountFactor(USD, PAY);
CurrencyParameterSensitivities sensiExpected =
FD_CAL.sensitivity(RATES_PROVIDER, p -> PRICER.presentValue(option, p, VOLS));
CurrencyParameterSensitivities sensiRes = FD_CAL.sensitivity(RATES_PROVIDER,
new Function<ImmutableRatesProvider, CurrencyAmount>() {
@Override
public CurrencyAmount apply(ImmutableRatesProvider p) {
double fwd = FX_PRICER.forwardFxRate(option.getUnderlying(), p).fxRate(CURRENCY_PAIR);
double[] strs = smileAtTime.strike(fwd).toArray();
double[] wghts = weights(fwd, strikeRate, strs, timeToExpiry, vols[1]);
double res = 0d;
for (int j = 0; j < 3; ++j) {
res += wghts[j] * (BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[j], true)
- BlackFormulaRepository.price(forwardRate, strs[j], timeToExpiry, vols[1], true));
}
return CurrencyAmount.of(USD, -res * df * NOTIONAL);
}
}
);
assertTrue(sensiComputed.equalWithTolerance(sensiExpected.combinedWith(sensiRes), FD_EPS * NOTIONAL * 10d));
}
}
public void test_presentValueSensitivity_afterExpiry() {
for (int i = 0; i < NB_STRIKES; ++i) {
PointSensitivityBuilder computedCall =
PRICER.presentValueSensitivityRatesStickyStrike(CALLS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedCall, PointSensitivityBuilder.none());
PointSensitivityBuilder computedPut =
PRICER.presentValueSensitivityRatesStickyStrike(PUTS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPut, PointSensitivityBuilder.none());
}
}
//-------------------------------------------------------------------------
public void test_presentValueSensitivityVolatility() {
for (int i = 0; i < NB_STRIKES; ++i) {
PointSensitivities computedCall =
PRICER.presentValueSensitivityModelParamsVolatility(CALLS[i], RATES_PROVIDER, VOLS).build();
double timeToExpiry = VOLS.relativeTime(EXPIRY);
FxRate forward = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER);
double forwardRate = forward.fxRate(CURRENCY_PAIR);
double strikeRate = CALLS[i].getStrike();
SmileDeltaParameters smileAtTime = VOLS.getSmile().smileForExpiry(timeToExpiry);
double[] strikes = smileAtTime.strike(forwardRate).toArray();
double[] vols = smileAtTime.getVolatility().toArray();
double df = RATES_PROVIDER.discountFactor(USD, PAY);
double[] weights = weights(forwardRate, strikeRate, strikes, timeToExpiry, vols[1]);
double[] vegas = new double[3];
vegas[2] = BlackFormulaRepository.vega(forwardRate, strikeRate, timeToExpiry, vols[1]) * df * NOTIONAL;
for (int j = 0; j < 3; j += 2) {
vegas[2] -= weights[j] * NOTIONAL * df *
BlackFormulaRepository.vega(forwardRate, strikes[j], timeToExpiry, vols[1]);
}
vegas[0] = weights[0] * NOTIONAL * df *
BlackFormulaRepository.vega(forwardRate, strikes[0], timeToExpiry, vols[0]);
vegas[1] = weights[2] * NOTIONAL * df *
BlackFormulaRepository.vega(forwardRate, strikes[2], timeToExpiry, vols[2]);
double[] expStrikes = new double[] {strikes[0], strikes[2], strikes[1] };
for (int j = 0; j < 3; ++j) {
FxOptionSensitivity sensi = (FxOptionSensitivity) computedCall.getSensitivities().get(j);
assertEquals(sensi.getSensitivity(), vegas[j], TOL * NOTIONAL);
assertEquals(sensi.getStrike(), expStrikes[j], TOL);
assertEquals(sensi.getForward(), forwardRate, TOL);
assertEquals(sensi.getCurrency(), USD);
assertEquals(sensi.getCurrencyPair(), CURRENCY_PAIR);
assertEquals(sensi.getExpiry(), timeToExpiry);
}
}
}
public void test_presentValueSensitivityVolatility_afterExpiry() {
for (int i = 0; i < NB_STRIKES; ++i) {
PointSensitivityBuilder computedCall =
PRICER.presentValueSensitivityModelParamsVolatility(CALLS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedCall, PointSensitivityBuilder.none());
PointSensitivityBuilder computedPut =
PRICER.presentValueSensitivityModelParamsVolatility(PUTS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPut, PointSensitivityBuilder.none());
}
}
//-------------------------------------------------------------------------
public void test_currencyExposure() {
for (int i = 0; i < NB_STRIKES; ++i) {
CurrencyAmount pvCall = PRICER.presentValue(CALLS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiCall = PRICER.presentValueSensitivityRatesStickyStrike(CALLS[i], RATES_PROVIDER, VOLS);
MultiCurrencyAmount computedCall = PRICER.currencyExposure(CALLS[i], RATES_PROVIDER, VOLS);
MultiCurrencyAmount expectedCall = RATES_PROVIDER.currencyExposure(pvSensiCall.build()).plus(pvCall);
assertEquals(computedCall.getAmount(EUR).getAmount(), expectedCall.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(computedCall.getAmount(USD).getAmount(), expectedCall.getAmount(USD).getAmount(), NOTIONAL * TOL);
CurrencyAmount pvPut = PRICER.presentValue(PUTS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiPut = PRICER.presentValueSensitivityRatesStickyStrike(PUTS[i], RATES_PROVIDER, VOLS);
MultiCurrencyAmount computedPut = PRICER.currencyExposure(PUTS[i], RATES_PROVIDER, VOLS);
MultiCurrencyAmount expectedPut = RATES_PROVIDER.currencyExposure(pvSensiPut.build()).plus(pvPut);
assertEquals(computedPut.getAmount(EUR).getAmount(), expectedPut.getAmount(EUR).getAmount(), NOTIONAL * TOL);
assertEquals(computedPut.getAmount(USD).getAmount(), expectedPut.getAmount(USD).getAmount(), NOTIONAL * TOL);
}
}
public void test_currencyExposure_atExpiry() {
for (int i = 0; i < NB_STRIKES; ++i) {
MultiCurrencyAmount computedCall = PRICER.currencyExposure(CALLS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedCall, MultiCurrencyAmount.empty());
MultiCurrencyAmount computedPut = PRICER.currencyExposure(PUTS[i], RATES_PROVIDER_AFTER, VOLS_AFTER);
assertEquals(computedPut, MultiCurrencyAmount.empty());
}
}
//-------------------------------------------------------------------------
public void test_putCallParity() {
double df = RATES_PROVIDER.discountFactor(USD, PAY);
PointSensitivityBuilder dfSensi = RATES_PROVIDER.discountFactors(USD).zeroRatePointSensitivity(PAY);
for (int i = 0; i < NB_STRIKES; ++i) {
CurrencyAmount pvCall = PRICER.presentValue(CALLS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiCall =
PRICER.presentValueSensitivityRatesStickyStrike(CALLS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiVolCall =
PRICER.presentValueSensitivityModelParamsVolatility(CALLS[i], RATES_PROVIDER, VOLS);
CurrencyAmount pvPut = PRICER.presentValue(PUTS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiPut =
PRICER.presentValueSensitivityRatesStickyStrike(PUTS[i], RATES_PROVIDER, VOLS);
PointSensitivityBuilder pvSensiVolPut =
PRICER.presentValueSensitivityModelParamsVolatility(PUTS[i], RATES_PROVIDER, VOLS);
double forward = FX_PRICER.forwardFxRate(UNDERLYING[i], RATES_PROVIDER).fxRate(CURRENCY_PAIR);
PointSensitivityBuilder forwardSensi = FX_PRICER.forwardFxRatePointSensitivity(UNDERLYING[i], RATES_PROVIDER);
double strike = CALLS[i].getStrike();
assertEquals(pvCall.getAmount() + pvPut.getAmount(), df * (forward - strike) * NOTIONAL, TOL * NOTIONAL);
assertTrue(pvSensiCall.combinedWith(pvSensiPut).build().normalized().equalWithTolerance(
dfSensi.multipliedBy((forward - strike) * NOTIONAL)
.combinedWith(forwardSensi.multipliedBy(df * NOTIONAL)).build().normalized(),
NOTIONAL * TOL));
DoubleArray sensiVol = VOLS.parameterSensitivity(
pvSensiVolCall.combinedWith(pvSensiVolPut).build()).getSensitivities().get(0).getSensitivity();
assertTrue(DoubleArrayMath.fuzzyEquals(sensiVol.toArray(), new double[sensiVol.size()], NOTIONAL * TOL));
}
}
//-------------------------------------------------------------------------
public void regression_test() {
double[] expected = new double[] {
3.860405407112769E7, 3.0897699603079587E7, 2.3542824458812844E7, 1.6993448607300103E7, 1.1705393621236656E7,
7865881.8260216825, 5312495.846331886, 3680367.6766224853, 2607701.430445888, 1849818.297903138, 1282881.9812227674 };
double[][][] sensiExpected = new double[][][] {
{ {0d, 0d, -1.016834993607875E8, -1.0687281893573801E8, 0d },
{0d, 0d, 7.321670893786977E7, 7.695325324735151E7, 0d } },
{ {0d, 0d, -1.0021953885887374E8, -1.0533414661787288E8, 0d },
{0d, 0d, 7.743544635059586E7, 8.13872898657015E7, 0d } },
{ {0d, 0d, -9.430418338541561E7, -9.911690666813123E7, 0d },
{0d, 0d, 7.69436064730077E7, 8.087034941308834E7, 0d } },
{ {0d, 0d, -8.284596766339977E7, -8.707393192902757E7, 0d },
{0d, 0d, 7.031492781003796E7, 7.3903382511043E7, 0d } },
{ {0d, 0d, -6.764082328040574E7, -7.109280762910862E7, 0d },
{0d, 0d, 5.900921722111582E7, 6.2020695857797466E7, 0d } },
{ {0d, 0d, -5.2035331262043096E7, -5.4690904337366335E7, 0d },
{0d, 0d, 4.623499720852033E7, 4.859455581508104E7, 0d } },
{ {0d, 0d, -3.862682913929568E7, -4.059811220715709E7, 0d },
{0d, 0d, 3.470937255296122E7, 3.64807319923551E7, 0d } },
{ {0d, 0d, -2.8260648102423556E7, -2.970290309286549E7, 0d },
{0d, 0d, 2.554672963322189E7, 2.6850482405254934E7, 0d } },
{ {0d, 0d, -2.0537629799871795E7, -2.1585747980437294E7, 0d },
{0d, 0d, 1.8614699892839946E7, 1.9564683195371673E7, 0d } },
{ {0d, 0d, -1.4728101851302534E7, -1.5479736361515924E7, 0d },
{0d, 0d, 1.3364038126029937E7, 1.404605895619165E7, 0d } },
{ {0d, 0d, -1.0288414551608022E7, -1.0813473891259879E7, 0d },
{0d, 0d, 9342412.029968219, 9819193.040939828, 0d } } };
double[][] sensiVolExpected = new double[][] {
{-5.026579681006058E7, 1.8086314260527827E7, 3.7857814067085885E7 },
{-8.042768121510313E7, 2.6917736783424407E7, 6.828128237717555E7 },
{-7.799249393870309E7, 2.3424689020542752E7, 8.3630726080757E7 },
{-3.164036884467365E7, 7924431.550466087, 6.934450937795731E7 },
{3.014881927958022E7, -5158819.178957329, 3.45255051012762E7 },
{6.557093411024924E7, -1634314.6028730718, 2572569.1856012754 },
{5.660208041880186E7, 1.8970507395428047E7, -1.03548720023163E7 },
{1.685473267352155E7, 4.502558010977008E7, -4691785.767471665 },
{-2.724527388461766E7, 6.375255786622942E7, 9031557.200953793 },
{-5.657559310469577E7, 6.925498619398344E7, 2.0671226006236725E7 },
{-6.6067357100129634E7, 6.307938934530911E7, 2.5692820222277485E7 } };
CurveName eurName = RatesProviderFxDataSets.getCurveName(EUR);
CurveName usdName = RatesProviderFxDataSets.getCurveName(USD);
for (int i = 0; i < NB_STRIKES; ++i) {
// pv
CurrencyAmount computed = PRICER.presentValue(CALLS[i], RATES_PROVIDER, VOLS);
assertEquals(computed.getAmount(), expected[i], NOTIONAL * TOL);
// curve sensitivity
PointSensitivityBuilder point = PRICER.presentValueSensitivityRatesStickyStrike(CALLS[i], RATES_PROVIDER, VOLS);
CurrencyParameterSensitivities sensiComputed = RATES_PROVIDER.parameterSensitivity(point.build());
assertTrue(DoubleArrayMath.fuzzyEquals(
sensiComputed.getSensitivity(eurName, USD).getSensitivity().toArray(),
sensiExpected[i][0],
NOTIONAL * TOL));
assertTrue(DoubleArrayMath.fuzzyEquals(
sensiComputed.getSensitivity(usdName, USD).getSensitivity().toArray(),
sensiExpected[i][1],
NOTIONAL * TOL));
// vol sensitivity
PointSensitivities pointVol =
PRICER.presentValueSensitivityModelParamsVolatility(CALLS[i], RATES_PROVIDER, VOLS).build();
assertEquals(pointVol.getSensitivities().get(0).getSensitivity(), sensiVolExpected[i][2], NOTIONAL * TOL);
assertEquals(pointVol.getSensitivities().get(1).getSensitivity(), sensiVolExpected[i][1], NOTIONAL * TOL);
assertEquals(pointVol.getSensitivities().get(2).getSensitivity(), sensiVolExpected[i][0], NOTIONAL * TOL);
}
}
//-------------------------------------------------------------------------
private double[] weights(double forward, double strike, double[] strikes, double timeToExpiry, double atmVol) {
double[][] mat = new double[3][3];
double[] vec = new double[3];
for (int i = 0; i < 3; ++i) {
mat[0][i] = BlackFormulaRepository.vega(forward, strikes[i], timeToExpiry, atmVol);
mat[1][i] = BlackFormulaRepository.vanna(forward, strikes[i], timeToExpiry, atmVol);
mat[2][i] = BlackFormulaRepository.volga(forward, strikes[i], timeToExpiry, atmVol);
}
vec[0] = BlackFormulaRepository.vega(forward, strike, timeToExpiry, atmVol);
vec[1] = BlackFormulaRepository.vanna(forward, strike, timeToExpiry, atmVol);
vec[2] = BlackFormulaRepository.volga(forward, strike, timeToExpiry, atmVol);
DecompositionResult res = SVD.apply(DoubleMatrix.ofUnsafe(mat));
return res.solve(vec);
}
}