/** * Copyright (C) 2016 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ 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 org.testng.Assert.assertEquals; import java.time.LocalDate; import java.time.ZoneId; import java.time.ZonedDateTime; import org.testng.annotations.Test; import com.opengamma.strata.basics.currency.CurrencyAmount; import com.opengamma.strata.pricer.fx.RatesProviderFxDataSets; import com.opengamma.strata.pricer.impl.option.BlackFormulaRepository; import com.opengamma.strata.pricer.impl.tree.EuropeanVanillaOptionFunction; import com.opengamma.strata.pricer.impl.tree.OptionFunction; import com.opengamma.strata.pricer.impl.tree.TrinomialTree; import com.opengamma.strata.pricer.rate.ImmutableRatesProvider; import com.opengamma.strata.product.common.LongShort; import com.opengamma.strata.product.common.PutCall; import com.opengamma.strata.product.fx.ResolvedFxSingle; import com.opengamma.strata.product.fxopt.ResolvedFxVanillaOption; /** * Test {@link ImpliedTrinomialTreeFxOptionCalibrator}. * <p> * Further tests with barrier options are in {@link ImpliedTrinomialTreeFxSingleBarrierOptionProductPricerTest}. */ @Test public class ImpliedTrinomialTreeFxOptionCalibratorTest { private static final ZoneId ZONE = ZoneId.of("Z"); private static final LocalDate VAL_DATE = LocalDate.of(2011, 6, 13); private static final ZonedDateTime VAL_DATETIME = VAL_DATE.atStartOfDay(ZONE); private static final LocalDate PAY_DATE = LocalDate.of(2012, 9, 15); private static final LocalDate EXPIRY_DATE = LocalDate.of(2012, 9, 15); private static final ZonedDateTime EXPIRY_DATETIME = EXPIRY_DATE.atStartOfDay(ZONE); // providers private static final BlackFxOptionSmileVolatilities VOLS = FxVolatilitySmileDataSet.createVolatilitySmileProvider5(VAL_DATETIME); private static final BlackFxOptionSmileVolatilities VOLS_MRKT = FxVolatilitySmileDataSet.createVolatilitySmileProvider5Market(VAL_DATETIME); private static final ImmutableRatesProvider RATE_PROVIDER = RatesProviderFxDataSets.createProviderEurUsdFlat(VAL_DATE); // call - for calibration private static final double NOTIONAL = 100_000_000d; private static final double STRIKE_RATE = 1.35; private static final CurrencyAmount EUR_AMOUNT_REC = CurrencyAmount.of(EUR, NOTIONAL); private static final CurrencyAmount USD_AMOUNT_PAY = CurrencyAmount.of(USD, -NOTIONAL * STRIKE_RATE); private static final ResolvedFxSingle FX_PRODUCT = ResolvedFxSingle.of(EUR_AMOUNT_REC, USD_AMOUNT_PAY, PAY_DATE); private static final ResolvedFxVanillaOption CALL = ResolvedFxVanillaOption.builder() .longShort(LongShort.LONG) .expiry(EXPIRY_DATETIME) .underlying(FX_PRODUCT) .build(); private static final ImpliedTrinomialTreeFxOptionCalibrator CALIB = new ImpliedTrinomialTreeFxOptionCalibrator(39); private static final RecombiningTrinomialTreeData TREE_DATA = CALIB.calibrateTrinomialTree(CALL, RATE_PROVIDER, VOLS); private static final RecombiningTrinomialTreeData TREE_DATA_MRKT = CALIB.calibrateTrinomialTree(CALL, RATE_PROVIDER, VOLS_MRKT); private static final TrinomialTree TREE = new TrinomialTree(); public void test_recoverVolatility() { int nSteps = TREE_DATA.getNumberOfSteps(); double spot = TREE_DATA.getSpot(); double timeToExpiry = TREE_DATA.getTime(nSteps); double dfDom = RATE_PROVIDER.discountFactors(USD).discountFactor(timeToExpiry); double dfFor = RATE_PROVIDER.discountFactors(EUR).discountFactor(timeToExpiry); double forward = spot * dfFor / dfDom; for (int i = 0; i < 100; ++i) { double strike = spot * (0.8 + 0.004 * i); OptionFunction func = EuropeanVanillaOptionFunction.of(strike, timeToExpiry, PutCall.CALL, nSteps); double price = TREE.optionPrice(func, TREE_DATA); double impliedVol = BlackFormulaRepository.impliedVolatility(price / dfDom, forward, strike, timeToExpiry, true); double orgVol = VOLS.volatility(FX_PRODUCT.getCurrencyPair(), timeToExpiry, strike, forward); assertEquals(impliedVol, orgVol, orgVol * 0.1); // large tol double priceMrkt = TREE.optionPrice(func, TREE_DATA_MRKT); double impliedVolMrkt = BlackFormulaRepository.impliedVolatility(priceMrkt / dfDom, forward, strike, timeToExpiry, true); double orgVolMrkt = VOLS_MRKT.volatility(FX_PRODUCT.getCurrencyPair(), timeToExpiry, strike, forward); assertEquals(impliedVolMrkt, orgVolMrkt, orgVolMrkt * 0.1); // large tol } } }