package com.opengamma.analytics.financial.forex.provider; import static com.opengamma.util.money.Currency.AUD; import static com.opengamma.util.money.Currency.EUR; import static com.opengamma.util.money.Currency.NZD; import static com.opengamma.util.money.Currency.USD; import static org.testng.Assert.assertEquals; import static org.threeten.bp.temporal.ChronoField.DAY_OF_MONTH; import static org.threeten.bp.temporal.ChronoField.MONTH_OF_YEAR; import static org.threeten.bp.temporal.ChronoField.YEAR; import org.testng.annotations.Test; import org.threeten.bp.LocalDate; import org.threeten.bp.LocalTime; import org.threeten.bp.Period; import org.threeten.bp.ZoneId; import org.threeten.bp.ZonedDateTime; import org.threeten.bp.format.DateTimeFormatter; import org.threeten.bp.format.DateTimeFormatterBuilder; import com.opengamma.analytics.financial.datasets.CalendarUSD; import com.opengamma.analytics.financial.forex.definition.ForexDefinition; import com.opengamma.analytics.financial.forex.definition.ForexOptionDigitalDefinition; import com.opengamma.analytics.financial.forex.derivative.ForexOptionDigital; import com.opengamma.analytics.financial.forex.method.FXMatrix; import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilityNodeSensitivityDataBundle; import com.opengamma.analytics.financial.forex.method.PresentValueForexBlackVolatilitySensitivity; import com.opengamma.analytics.financial.model.interestrate.curve.YieldCurve; import com.opengamma.analytics.financial.model.option.definition.SmileDeltaParameters; import com.opengamma.analytics.financial.model.volatility.surface.SmileDeltaTermStructureParametersStrikeInterpolation; import com.opengamma.analytics.financial.provider.calculator.blackforex.PresentValueCurveSensitivityForexStaticReplicationSmileCalculator; import com.opengamma.analytics.financial.provider.description.forex.BlackForexSmileProviderDiscount; import com.opengamma.analytics.financial.provider.description.forex.BlackForexSmileProviderInterface; import com.opengamma.analytics.financial.provider.description.interestrate.MulticurveProviderDiscount; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyMulticurveSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.multicurve.MultipleCurrencyParameterSensitivity; import com.opengamma.analytics.financial.provider.sensitivity.parameter.ParameterSensitivityParameterCalculator; import com.opengamma.analytics.math.curve.InterpolatedDoublesCurve; import com.opengamma.analytics.math.interpolation.CombinedInterpolatorExtrapolatorFactory; import com.opengamma.analytics.math.interpolation.Interpolator1D; import com.opengamma.analytics.math.interpolation.Interpolator1DFactory; import com.opengamma.analytics.math.interpolation.data.Interpolator1DDataBundle; import com.opengamma.analytics.util.time.TimeCalculator; import com.opengamma.financial.convention.businessday.BusinessDayConvention; import com.opengamma.financial.convention.businessday.BusinessDayConventions; import com.opengamma.financial.convention.calendar.Calendar; import com.opengamma.financial.convention.daycount.DayCount; import com.opengamma.financial.convention.daycount.DayCounts; import com.opengamma.util.money.Currency; import com.opengamma.util.money.CurrencyAmount; import com.opengamma.util.money.MultipleCurrencyAmount; import com.opengamma.util.time.DateUtils; import com.opengamma.util.tuple.Pairs; /** * End-to-end test for FX digital options. */ @Test @SuppressWarnings("unused") public class ForexOptionDigitalE2ETest { // interpolator for yield curve private static final Interpolator1D LINEAR_FLAT = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.LINEAR, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.FLAT_EXTRAPOLATOR); // interpolator for forward curve, which is used to estimate CNH discount curve private static final Interpolator1D CUBIC_FLAT_LINEAR = CombinedInterpolatorExtrapolatorFactory.getInterpolator( Interpolator1DFactory.NATURAL_CUBIC_MONOTONE, Interpolator1DFactory.FLAT_EXTRAPOLATOR, Interpolator1DFactory.LINEAR_EXTRAPOLATOR); private static final DayCount DAY_COUNT = DayCounts.ACT_365; private static final Calendar CALENDAR = new CalendarUSD("USD calendar"); protected static final BusinessDayConvention MOD_FOLLOWING = BusinessDayConventions.MODIFIED_FOLLOWING; /** * Spot and forward data. */ private static final LocalDate VALUATION_DATE = LocalDate.of(2015, 8, 6); private static final ZonedDateTime VALUATION_DATETIME = ZonedDateTime.of(VALUATION_DATE, LocalTime.of(13, 11), ZoneId.of("GMT-4")); private static final double SPOT_NZDUSD = 0.6538; private static final double SPOT_USDCNH = 6.2191; private static final double SPOT_AUDUSD = 0.73805; private static final double SPOT_EURUSD = 1.08815; private static final double[] FORWARDS_USDCNH = new double[] {6.23396, 6.2481, 6.2606, 6.3101, 6.34985, 6.3991, 6.5591 }; // Forward used to compute the discounting curve in CNH private static final DateTimeFormatter DDMMYYYY = new DateTimeFormatterBuilder().appendValue(DAY_OF_MONTH, 2) .appendLiteral("/").appendValue(MONTH_OF_YEAR, 2).appendLiteral("/").appendValue(YEAR, 4).toFormatter(); private static final LocalDate[] DATES_USDCNH = new LocalDate[] {LocalDate.parse("08/09/2015", DDMMYYYY), LocalDate.parse("06/10/2015", DDMMYYYY), LocalDate.parse("06/11/2015", DDMMYYYY), LocalDate.parse("16/02/2016", DDMMYYYY), LocalDate.parse("06/05/2016", DDMMYYYY), LocalDate.parse("08/08/2016", DDMMYYYY), LocalDate.parse("07/08/2017", DDMMYYYY) }; private static final Currency CNH = Currency.of("CNH"); private static final FXMatrix FX_MATRIX; static { FX_MATRIX = new FXMatrix(EUR, USD, SPOT_EURUSD); FX_MATRIX.addCurrency(NZD, USD, SPOT_NZDUSD); FX_MATRIX.addCurrency(CNH, USD, 1d / SPOT_USDCNH); FX_MATRIX.addCurrency(AUD, USD, SPOT_AUDUSD); } private static final MulticurveProviderDiscount MULTICURVE = new MulticurveProviderDiscount(FX_MATRIX); /** * Discount curve data. * CNH curve is estimated from USDCNH forward rates and USD discount curve. */ private static final String USD_DSC_NAME = "USD Dsc"; private static final String AUD_DSC_NAME = "AUD Dsc"; private static final String NZD_DSC_NAME = "NZD Dsc"; private static final String EUR_DSC_NAME = "EUR Dsc"; private static final String CNH_DSC_NAME = "CNH Dsc"; private static final double[] RATES_USD = new double[] {0.00126, 0.001505, 0.001915, 0.0025375, 0.003114, 0.003947, 0.005536, 0.007271, 0.0092, 0.011275, 0.013347, 0.015062, 0.016723, 0.0128225, 0.0154065, 0.017465, 0.019145, 0.020495, 0.0215865, 0.022466, 0.023212 }; // Zero coupon rates private static final double[] RATES_AUD = new double[] {0.0205, 0.0211, 0.0215, 0.0226, 0.02241, 0.02215, 0.02198, 0.02247, 0.02275, 0.0237, 0.02495, 0.026463, 0.027913, 0.0291065, 0.030225, 0.031125, 0.031945 }; private static final double[] RATES_NZD = new double[] {0.03, 0.031, 0.0306, 0.0302, 0.028896, 0.028083, 0.027764, 0.027939, 0.028723, 0.02895, 0.029725, 0.03075, 0.032, 0.0344, 0.0371 }; private static final double[] RATES_EUR = new double[] {-0.0018, -0.00133, -0.0008, 0.00047, 0.00046, 0.00047, 0.0004, 0.000545, 0.00095, 0.00178, 0.002905, 0.004195, 0.00557, 0.00695, 0.008255, 0.00945, 0.010485 }; private static final LocalDate[] DATES_USD = new LocalDate[] {DateUtils.toLocalDate("20150807"), DateUtils.toLocalDate("20150817"), DateUtils.toLocalDate("20150910"), DateUtils.toLocalDate("20151013"), DateUtils.toLocalDate("20151110"), DateUtils.toLocalDate("20151216"), DateUtils.toLocalDate("20160316"), DateUtils.toLocalDate("20160615"), DateUtils.toLocalDate("20160921"), DateUtils.toLocalDate("20161221"), DateUtils.toLocalDate("20170315"), DateUtils.toLocalDate("20170621"), DateUtils.toLocalDate("20170920"), DateUtils.toLocalDate("20180810"), DateUtils.toLocalDate("20190810"), DateUtils.toLocalDate("20200810"), DateUtils.toLocalDate("20210810"), DateUtils.toLocalDate("20220810"), DateUtils.toLocalDate("20230810"), DateUtils.toLocalDate("20240810"), DateUtils.toLocalDate("20250810") }; private static final LocalDate[] DATES_AUD = new LocalDate[] {DateUtils.toLocalDate("20150907"), DateUtils.toLocalDate("20151007"), DateUtils.toLocalDate("20151109"), DateUtils.toLocalDate("20160208"), DateUtils.toLocalDate("20160307"), DateUtils.toLocalDate("20160509"), DateUtils.toLocalDate("20160808"), DateUtils.toLocalDate("20170207"), DateUtils.toLocalDate("20170807"), DateUtils.toLocalDate("20180807"), DateUtils.toLocalDate("20190807"), DateUtils.toLocalDate("20200807"), DateUtils.toLocalDate("20210807"), DateUtils.toLocalDate("20220807"), DateUtils.toLocalDate("20230807"), DateUtils.toLocalDate("20240807"), DateUtils.toLocalDate("20250807") }; private static final LocalDate[] DATES_NZD = new LocalDate[] {DateUtils.toLocalDate("20150807"), DateUtils.toLocalDate("20150910"), DateUtils.toLocalDate("20151012"), DateUtils.toLocalDate("20151110"), DateUtils.toLocalDate("20151218"), DateUtils.toLocalDate("20160318"), DateUtils.toLocalDate("20160617"), DateUtils.toLocalDate("20160916"), DateUtils.toLocalDate("20160810"), DateUtils.toLocalDate("20170810"), DateUtils.toLocalDate("20180810"), DateUtils.toLocalDate("20190810"), DateUtils.toLocalDate("20200810"), DateUtils.toLocalDate("20220810"), DateUtils.toLocalDate("20250810") }; private static final LocalDate[] DATES_EUR = new LocalDate[] {DateUtils.toLocalDate("20150807"), DateUtils.toLocalDate("20150817"), DateUtils.toLocalDate("20150910"), DateUtils.toLocalDate("20160210"), DateUtils.toLocalDate("20160310"), DateUtils.toLocalDate("20160411"), DateUtils.toLocalDate("20160510"), DateUtils.toLocalDate("20160810"), DateUtils.toLocalDate("20170810"), DateUtils.toLocalDate("20180810"), DateUtils.toLocalDate("20190810"), DateUtils.toLocalDate("20200810"), DateUtils.toLocalDate("20210810"), DateUtils.toLocalDate("20220810"), DateUtils.toLocalDate("20230810"), DateUtils.toLocalDate("20240810"), DateUtils.toLocalDate("20250810") }; private static final double[] RATES_CNH; static { // forward curve of USD/CNH, used for estimating CNH discount curve. double[] timeFwd = new double[FORWARDS_USDCNH.length + 1]; double[] fwd = new double[FORWARDS_USDCNH.length + 1]; for (int i = 0; i < FORWARDS_USDCNH.length; ++i) { timeFwd[i + 1] = DAY_COUNT.getDayCountFraction(VALUATION_DATE, DATES_USDCNH[i]); fwd[i + 1] = FORWARDS_USDCNH[i]; } fwd[0] = SPOT_USDCNH; Interpolator1DDataBundle bundle = CUBIC_FLAT_LINEAR.getDataBundle(timeFwd, fwd); // discount curves RATES_CNH = new double[RATES_USD.length]; String[] names = new String[] {USD_DSC_NAME, AUD_DSC_NAME, NZD_DSC_NAME, EUR_DSC_NAME }; double[][] rates = new double[][] {RATES_USD, RATES_AUD, RATES_NZD, RATES_EUR }; LocalDate[][] dates = new LocalDate[][] {DATES_USD, DATES_AUD, DATES_NZD, DATES_EUR }; YieldCurve[] curves = new YieldCurve[5]; for (int i = 0; i < 4; ++i) { int n = rates[i].length; double[] times = new double[n]; for (int j = 0; j < n; ++j) { times[j] = TimeCalculator.getTimeBetween(VALUATION_DATE, dates[i][j]); if (i == 0) { RATES_CNH[j] = RATES_USD[j] + Math.log(CUBIC_FLAT_LINEAR.interpolate(bundle, times[j]) / SPOT_USDCNH) / times[j]; } } curves[i] = new YieldCurve(names[i], new InterpolatedDoublesCurve(times, rates[i], LINEAR_FLAT, true, names[i])); if (i == 0) { curves[4] = new YieldCurve( CNH_DSC_NAME, new InterpolatedDoublesCurve(times, RATES_CNH, LINEAR_FLAT, true, CNH_DSC_NAME)); } } MULTICURVE.setCurve(USD, curves[0]); MULTICURVE.setCurve(AUD, curves[1]); MULTICURVE.setCurve(NZD, curves[2]); MULTICURVE.setCurve(EUR, curves[3]); MULTICURVE.setCurve(CNH, curves[4]); } /** * Vol surface data */ private static final double[] DELTAS = new double[] {0.05, 0.1, 0.25 }; private static final double[][] VOLS_NZDUSD = new double[][] { {20.28, 19.77, 19.14, 18.69, 18.62, 18.83, 19.07 }, {15.99, 15.45, 14.79, 14.3, 14.19, 14.36, 14.57 }, {14.72, 14.14, 13.43, 12.88, 12.72, 12.86, 13.05 }, {14.76, 14.15, 13.4, 12.8, 12.6, 12.69, 12.85 }, {14.92, 14.28, 13.47, 12.8, 12.52, 12.55, 12.67 }, {15.77, 14.9, 13.83, 12.98, 12.63, 12.7, 12.89 }, {16.17, 15.05, 13.75, 12.78, 12.4, 12.57, 12.89 }, {16.37, 15.1, 13.67, 12.62, 12.23, 12.46, 12.85 }, {16.64, 15.21, 13.64, 12.51, 12.09, 12.39, 12.87 }, {16.98, 15.38, 13.66, 12.45, 12.01, 12.38, 12.96 }, {17.52, 15.68, 13.71, 12.32, 11.81, 12.21, 12.85 }, {17.8, 15.86, 13.79, 12.35, 11.79, 12.19, 12.86 }, {18.71, 16.37, 13.99, 12.5, 11.94, 12.46, 13.33 }, {18.99, 16.55, 14.11, 12.58, 12.01, 12.56, 13.46 }, {20.11, 17.28, 14.5, 12.88, 12.25, 12.84, 13.86 }, {20.94, 17.8, 14.84, 13.12, 12.46, 13.2, 14.45 }, {21.71, 18.33, 15.21, 13.42, 12.71, 13.56, 14.98 }, {21.75, 18.77, 15.48, 13.5, 12.73, 12.84, 13.4 }, {22.56, 19.38, 15.77, 13.55, 12.65, 12.69, 13.22 } }; private static final double[][] VOLS_USDCNH = new double[][] { {3.01, 2.12, 1.23, 1.5, 2.37, 3.99, 5.1 }, {2.96, 2.07, 1.22, 1.55, 2.48, 4.09, 5.27 }, {2.96, 2.06, 1.26, 1.65, 2.64, 4.28, 5.55 }, {2.86, 1.98, 1.23, 1.69, 2.74, 4.36, 5.69 }, {2.74, 1.88, 1.16, 1.7, 2.86, 4.45, 5.82 }, {3.01, 2.11, 1.38, 2, 3.28, 5.08, 6.64 }, {3.42, 2.47, 1.78, 2.45, 3.83, 5.89, 7.65 }, {4.03, 3.01, 2.29, 2.98, 4.43, 6.76, 8.72 }, {4.32, 3.28, 2.57, 3.29, 4.8, 7.29, 9.36 }, {4.5, 3.45, 2.75, 3.5, 5.1, 7.73, 9.89 }, {5.04, 3.89, 3.15, 4, 5.8, 8.72, 11.08 }, {5.46, 4.24, 3.46, 4.45, 6.51, 9.75, 12.29 }, {6.64, 5.28, 4.44, 5.47, 7.71, 11.39, 14.27 }, {7.22, 5.78, 4.88, 5.95, 8.38, 12.42, 15.53 } }; private static final double[][] VOLS_AUDUSD = new double[][] { {14.04, 13.6, 13.06, 12.66, 12.67, 12.89, 13.12 }, {13.98, 13.52, 12.93, 12.48, 12.42, 12.6, 12.79 }, {13.39, 12.87, 12.21, 11.69, 11.58, 11.72, 11.88 }, {13.6, 13.04, 12.32, 11.73, 11.56, 11.65, 11.78 }, {13.91, 13.29, 12.49, 11.8, 11.54, 11.56, 11.64 }, {14.1, 13.41, 12.5, 11.7, 11.34, 11.28, 11.31 }, {14.99, 13.97, 12.72, 11.75, 11.42, 11.54, 11.77 }, {15.19, 14.08, 12.74, 11.7, 11.33, 11.47, 11.73 }, {15.43, 14.22, 12.79, 11.66, 11.25, 11.41, 11.71 }, {15.69, 14.39, 12.85, 11.65, 11.2, 11.39, 11.72 }, {16.37, 14.8, 13.01, 11.65, 11.16, 11.4, 11.82 }, {17.03, 15.21, 13.16, 11.68, 11.16, 11.48, 12.01 }, {17.95, 15.73, 13.39, 11.84, 11.31, 11.78, 12.54 }, {18.38, 15.97, 13.53, 11.92, 11.38, 11.93, 12.8 }, {19.38, 16.62, 13.9, 12.23, 11.6, 12.19, 13.2 }, {20.08, 17.15, 14.3, 12.58, 11.9, 12.52, 13.6 }, {20.9, 17.78, 14.72, 12.88, 12.07, 12.66, 13.76 }, {21.59, 18.48, 15.08, 12.96, 11.93, 12.1, 12.81 }, {23.22, 19.6, 15.52, 13.02, 11.63, 11.68, 12.43 } }; private static final double[][] VOLS_EURUSD = new double[][] { {12.11, 11.74, 11.29, 10.99, 11.09, 11.39, 11.68 }, {12.19, 11.8, 11.33, 11, 11.07, 11.37, 11.65 }, {11.72, 11.3, 10.8, 10.44, 10.49, 10.77, 11.04 }, {11.81, 11.37, 10.84, 10.45, 10.48, 10.74, 11 }, {11.98, 11.51, 10.94, 10.5, 10.49, 10.74, 10.99 }, {12.36, 11.76, 11.04, 10.45, 10.34, 10.55, 10.79 }, {12.42, 11.71, 10.87, 10.2, 10.02, 10.23, 10.49 }, {12.6, 11.8, 10.86, 10.12, 9.9, 10.11, 10.39 }, {12.85, 11.93, 10.88, 10.07, 9.79, 10, 10.31 }, {13.13, 12.09, 10.93, 10.05, 9.73, 9.94, 10.27 }, {13.27, 12.17, 10.91, 9.95, 9.56, 9.71, 10 }, {13.51, 12.36, 11, 9.95, 9.5, 9.58, 9.82 }, {13.41, 12.21, 10.85, 9.83, 9.4, 9.53, 9.84 }, {13.39, 12.13, 10.76, 9.77, 9.36, 9.55, 9.92 }, {13.31, 12.07, 10.75, 9.85, 9.5, 9.76, 10.2 }, {13.08, 11.9, 10.67, 9.85, 9.57, 9.87, 10.34 }, {12.96, 11.84, 10.67, 9.9, 9.67, 9.99, 10.47 }, {11.94, 11.37, 10.59, 9.95, 9.84, 9.92, 10.01 }, {11.76, 11.19, 10.51, 10.05, 10.11, 10.39, 10.67 } }; private static final Period[] PERIODS_NZDUSD = new Period[] {Period.ofDays(1), Period.ofDays(7), Period.ofDays(14), Period.ofDays(21), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(4), Period.ofMonths(5), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; private static final Period[] PERIODS_USDCNH = new Period[] {Period.ofDays(1), Period.ofDays(7), Period.ofDays(14), Period.ofDays(21), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(4), Period.ofMonths(5), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2) }; private static final Period[] PERIODS_AUDUSD = new Period[] {Period.ofDays(1), Period.ofDays(7), Period.ofDays(14), Period.ofDays(21), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(4), Period.ofMonths(5), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; private static final Period[] PERIODS_EURUSD = new Period[] {Period.ofDays(1), Period.ofDays(7), Period.ofDays(14), Period.ofDays(21), Period.ofMonths(1), Period.ofMonths(2), Period.ofMonths(3), Period.ofMonths(4), Period.ofMonths(5), Period.ofMonths(6), Period.ofMonths(9), Period.ofYears(1), Period.ofMonths(18), Period.ofYears(2), Period.ofYears(3), Period.ofYears(4), Period.ofYears(5), Period.ofYears(7), Period.ofYears(10) }; private static final SmileDeltaTermStructureParametersStrikeInterpolation VOL_SURFACE_NZDUSD; private static final SmileDeltaTermStructureParametersStrikeInterpolation VOL_SURFACE_USDCNH; private static final SmileDeltaTermStructureParametersStrikeInterpolation VOL_SURFACE_AUDUSD; private static final SmileDeltaTermStructureParametersStrikeInterpolation VOL_SURFACE_EURUSD; static { double[][][] vols = new double[][][] {VOLS_NZDUSD, VOLS_USDCNH, VOLS_AUDUSD, VOLS_EURUSD }; Period[][] periods = new Period[][] {PERIODS_NZDUSD, PERIODS_USDCNH, PERIODS_AUDUSD, PERIODS_EURUSD }; SmileDeltaParameters[][] term = new SmileDeltaParameters[4][]; for (int i = 0; i < 4; ++i) { int n = periods[i].length; term[i] = new SmileDeltaParameters[n]; for (int j = 0; j < n; ++j) { double time = DAY_COUNT.getDayCountFraction( VALUATION_DATE, MOD_FOLLOWING.adjustDate(CALENDAR, VALUATION_DATE.plus(periods[i][j]))); for (int k = 0; k < vols[i][j].length; ++k) { vols[i][j][k] *= 0.01; } term[i][j] = new SmileDeltaParameters(time, DELTAS, vols[i][j]); } } VOL_SURFACE_NZDUSD = new SmileDeltaTermStructureParametersStrikeInterpolation(term[0]); VOL_SURFACE_USDCNH = new SmileDeltaTermStructureParametersStrikeInterpolation(term[1]); VOL_SURFACE_AUDUSD = new SmileDeltaTermStructureParametersStrikeInterpolation(term[2]); VOL_SURFACE_EURUSD = new SmileDeltaTermStructureParametersStrikeInterpolation(term[3]); } private static final BlackForexSmileProviderDiscount PROVIDER_NZDUSD = new BlackForexSmileProviderDiscount(MULTICURVE, VOL_SURFACE_NZDUSD, Pairs.of(NZD, USD)); private static final BlackForexSmileProviderDiscount PROVIDER_USDCNH = new BlackForexSmileProviderDiscount(MULTICURVE, VOL_SURFACE_USDCNH, Pairs.of(USD, CNH)); private static final BlackForexSmileProviderDiscount PROVIDER_AUDUSD = new BlackForexSmileProviderDiscount(MULTICURVE, VOL_SURFACE_AUDUSD, Pairs.of(AUD, USD)); private static final BlackForexSmileProviderDiscount PROVIDER_EURUSD = new BlackForexSmileProviderDiscount(MULTICURVE, VOL_SURFACE_EURUSD, Pairs.of(EUR, USD)); /** * Instruments. */ private static final double STRIKE_AUDUSD = 0.72; private static final double STRIKE_EURUSD = 1.05; private static final double STRIKE_NZDUSD = 0.6; private static final double STRIKE_USDCNH = 6.2165; private static final double SETTLE_CURRENCY_NOTIONAL = 1.0e6; // put on AUD/USD, settle in USD private static final ZonedDateTime EXPIRY_AUDUSD = ZonedDateTime.of(2015, 11, 10, 10, 0, 0, 0, ZoneId.of("GMT+9")); private static final ZonedDateTime SETTLE_AUDUSD = MOD_FOLLOWING.adjustDate(CALENDAR, EXPIRY_AUDUSD.plusDays(2)); private static final ForexDefinition FX_AUDUSD = new ForexDefinition(AUD, USD, SETTLE_AUDUSD, SETTLE_CURRENCY_NOTIONAL / STRIKE_AUDUSD, STRIKE_AUDUSD); private static final ForexOptionDigitalDefinition DEFINITION_AUDUSD = new ForexOptionDigitalDefinition(FX_AUDUSD, EXPIRY_AUDUSD, false, true, true); private static final ForexOptionDigital DERIVATIVE_AUDUSD = DEFINITION_AUDUSD.toDerivative(VALUATION_DATETIME); //put on EUR/USD, settle in USD private static final ZonedDateTime EXPIRY_EURUSD = ZonedDateTime.of(2015, 8, 13, 10, 0, 0, 0, ZoneId.of("GMT-4")); private static final ZonedDateTime SETTLE_EURUSD = MOD_FOLLOWING.adjustDate(CALENDAR, EXPIRY_EURUSD.plusDays(2)); private static final ForexDefinition FX_EURUSD = new ForexDefinition(EUR, USD, SETTLE_EURUSD, SETTLE_CURRENCY_NOTIONAL / STRIKE_EURUSD, STRIKE_EURUSD); private static final ForexOptionDigitalDefinition DEFINITION_EURUSD = new ForexOptionDigitalDefinition(FX_EURUSD, EXPIRY_EURUSD, false, true, true); private static final ForexOptionDigital DERIVATIVE_EURUSD = DEFINITION_EURUSD.toDerivative(VALUATION_DATETIME); //put on NZD/USD, settle in USD private static final ZonedDateTime EXPIRY_NZDUSD = ZonedDateTime.of(2015, 12, 17, 10, 0, 0, 0, ZoneId.of("GMT-4")); private static final ZonedDateTime SETTLE_NZDUSD = MOD_FOLLOWING.adjustDate(CALENDAR, EXPIRY_NZDUSD.plusDays(2)); private static final ForexDefinition FX_NZDUSD = new ForexDefinition(NZD, USD, SETTLE_NZDUSD, SETTLE_CURRENCY_NOTIONAL / STRIKE_NZDUSD, STRIKE_NZDUSD); private static final ForexOptionDigitalDefinition DEFINITION_NZDUSD = new ForexOptionDigitalDefinition(FX_NZDUSD, EXPIRY_NZDUSD, false, true, true); private static final ForexOptionDigital DERIVATIVE_NZDUSD = DEFINITION_NZDUSD.toDerivative(VALUATION_DATETIME); //put on USD/CNH, settle in USD private static final ZonedDateTime EXPIRY_CNHUSD = ZonedDateTime.of(2015, 8, 27, 15, 0, 0, 0, ZoneId.of("GMT+9")); private static final ZonedDateTime SETTLE_CNHUSD = MOD_FOLLOWING.adjustDate(CALENDAR, EXPIRY_CNHUSD.plusDays(2)); private static final ForexDefinition FX_CNHUSD = new ForexDefinition(USD, CNH, SETTLE_CNHUSD, SETTLE_CURRENCY_NOTIONAL, STRIKE_USDCNH); private static final ForexOptionDigitalDefinition DEFINITION_USDCNH = new ForexOptionDigitalDefinition(FX_CNHUSD, EXPIRY_CNHUSD, false, true, false); private static final ForexOptionDigital DERIVATIVE_USDCNH = DEFINITION_USDCNH.toDerivative(VALUATION_DATETIME); private static final ForexOptionDigitalCallSpreadBlackSmileMethod METHOD_SPREAD = new ForexOptionDigitalCallSpreadBlackSmileMethod(); private static final PresentValueCurveSensitivityForexStaticReplicationSmileCalculator PVSC = PresentValueCurveSensitivityForexStaticReplicationSmileCalculator.getInstance(); private static final ParameterSensitivityParameterCalculator<BlackForexSmileProviderInterface> PSC = new ParameterSensitivityParameterCalculator<>(PVSC); private static final double TOL = 1.0e-8; private static final boolean PRINT = false; public void testAUDUSD() { MultipleCurrencyAmount pv = METHOD_SPREAD.presentValue(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); MultipleCurrencyAmount ce = METHOD_SPREAD.currencyExposure(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); CurrencyAmount delta = METHOD_SPREAD.delta(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); CurrencyAmount gamma = METHOD_SPREAD.gamma(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); MultipleCurrencyMulticurveSensitivity pointSensi = METHOD_SPREAD.presentValueCurveSensitivity(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); MultipleCurrencyParameterSensitivity sensi = PSC.pointToParameterSensitivity(pointSensi, PROVIDER_AUDUSD); PresentValueForexBlackVolatilitySensitivity vega = METHOD_SPREAD.presentValueBlackVolatilitySensitivity(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); PresentValueForexBlackVolatilityNodeSensitivityDataBundle volSensi = METHOD_SPREAD.presentValueBlackVolatilityNodeSensitivity(DERIVATIVE_AUDUSD, PROVIDER_AUDUSD); assertEquals(pv.getAmount(USD), 342743.8725, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(delta.getAmount(), -8027937.8774, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(gamma.getAmount(), 81601543.8955, TOL * SETTLE_CURRENCY_NOTIONAL); if (PRINT) { System.out.println("PV: " + pv); System.out.println("Currency exposure: " + ce); System.out.println("PV delta: " + delta); System.out.println("PV gamma: " + gamma); System.out.println("Bucketed PV01 (zero-rates): " + sensi); System.out.println("PV vega: " + vega.getVega()); System.out.println("Bucketed PV vega:"); System.out.println(" absolute delta: " + volSensi.getDelta()); System.out.println(" time to expiry: " + volSensi.getExpiries()); System.out.println(" " + volSensi.getVega()); } } public void testEURUSD() { MultipleCurrencyAmount pv = METHOD_SPREAD.presentValue(DERIVATIVE_EURUSD, PROVIDER_EURUSD); MultipleCurrencyAmount ce = METHOD_SPREAD.currencyExposure(DERIVATIVE_EURUSD, PROVIDER_EURUSD); CurrencyAmount delta = METHOD_SPREAD.delta(DERIVATIVE_EURUSD, PROVIDER_EURUSD); CurrencyAmount gamma = METHOD_SPREAD.gamma(DERIVATIVE_EURUSD, PROVIDER_EURUSD); MultipleCurrencyMulticurveSensitivity pointSensi = METHOD_SPREAD.presentValueCurveSensitivity(DERIVATIVE_EURUSD, PROVIDER_EURUSD); MultipleCurrencyParameterSensitivity sensi = PSC.pointToParameterSensitivity(pointSensi, PROVIDER_EURUSD); PresentValueForexBlackVolatilitySensitivity vega = METHOD_SPREAD.presentValueBlackVolatilitySensitivity(DERIVATIVE_EURUSD, PROVIDER_EURUSD); PresentValueForexBlackVolatilityNodeSensitivityDataBundle volSensi = METHOD_SPREAD.presentValueBlackVolatilityNodeSensitivity(DERIVATIVE_EURUSD, PROVIDER_EURUSD); assertEquals(pv.getAmount(USD), 17397.77618436597, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(delta.getAmount(), -2340882.319637686, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(gamma.getAmount(), 2.7112557412446594E8, TOL * SETTLE_CURRENCY_NOTIONAL); boolean print = false; if (PRINT) { System.out.println("PV: " + pv); System.out.println("Currency exposure: " + ce); System.out.println("PV delta: " + delta); System.out.println("PV gamma: " + gamma); System.out.println("Bucketed PV01 (zero-rates): " + sensi); System.out.println("PV vega: " + vega.getVega()); System.out.println("Bucketed PV vega:"); System.out.println(" absolute delta: " + volSensi.getDelta()); System.out.println(" time to expiry: " + volSensi.getExpiries()); System.out.println(" " + volSensi.getVega()); } } public void testNZDUSD() { MultipleCurrencyAmount pv = METHOD_SPREAD.presentValue(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); MultipleCurrencyAmount ce = METHOD_SPREAD.currencyExposure(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); CurrencyAmount delta = METHOD_SPREAD.delta(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); CurrencyAmount gamma = METHOD_SPREAD.gamma(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); MultipleCurrencyMulticurveSensitivity pointSensi = METHOD_SPREAD.presentValueCurveSensitivity(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); MultipleCurrencyParameterSensitivity sensi = PSC.pointToParameterSensitivity(pointSensi, PROVIDER_NZDUSD); PresentValueForexBlackVolatilitySensitivity vega = METHOD_SPREAD.presentValueBlackVolatilitySensitivity(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); PresentValueForexBlackVolatilityNodeSensitivityDataBundle volSensi = METHOD_SPREAD.presentValueBlackVolatilityNodeSensitivity(DERIVATIVE_NZDUSD, PROVIDER_NZDUSD); assertEquals(pv.getAmount(USD), 157169.3731, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(delta.getAmount(), -4326631.7014, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(gamma.getAmount(), 84674218.4853, TOL * SETTLE_CURRENCY_NOTIONAL); if (PRINT) { System.out.println("PV: " + pv); System.out.println("Currency exposure: " + ce); System.out.println("Bucketed PV01 (zero-rates): " + sensi); System.out.println("PV gamma: " + gamma); System.out.println("Bucketed PV01: " + sensi); System.out.println("PV vega: " + vega.getVega()); System.out.println("Bucketed PV vega:"); System.out.println(" absolute delta: " + volSensi.getDelta()); System.out.println(" time to expiry: " + volSensi.getExpiries()); System.out.println(" " + volSensi.getVega()); } } public void testUSDCNH() { // spread is increased from the default (1.e-4) because of small CNH/USD rate. ForexOptionDigitalCallSpreadBlackSmileMethod method = new ForexOptionDigitalCallSpreadBlackSmileMethod(0.001); MultipleCurrencyAmount pv = method.presentValue(DERIVATIVE_USDCNH, PROVIDER_USDCNH); MultipleCurrencyAmount ce = method.currencyExposure(DERIVATIVE_USDCNH, PROVIDER_USDCNH); CurrencyAmount delta = method.delta(DERIVATIVE_USDCNH, PROVIDER_USDCNH); CurrencyAmount gamma = method.gamma(DERIVATIVE_USDCNH, PROVIDER_USDCNH); MultipleCurrencyMulticurveSensitivity pointSensi = method.presentValueCurveSensitivity(DERIVATIVE_USDCNH, PROVIDER_USDCNH); MultipleCurrencyParameterSensitivity sensi = PSC.pointToParameterSensitivity(pointSensi, PROVIDER_USDCNH); PresentValueForexBlackVolatilitySensitivity vega = method.presentValueBlackVolatilitySensitivity(DERIVATIVE_USDCNH, PROVIDER_USDCNH); PresentValueForexBlackVolatilityNodeSensitivityDataBundle volSensi = method.presentValueBlackVolatilityNodeSensitivity(DERIVATIVE_USDCNH, PROVIDER_USDCNH); assertEquals(pv.getAmount(USD), 247568.94182499617, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(delta.getAmount(), -1.4636480454570957E7, TOL * SETTLE_CURRENCY_NOTIONAL); assertEquals(gamma.getAmount(), 4.626467186570426E8, TOL * SETTLE_CURRENCY_NOTIONAL); if (PRINT) { System.out.println("PV: " + pv); System.out.println("Currency exposure: " + ce); System.out.println("Bucketed PV01 (zero-rates): " + sensi); System.out.println("PV gamma: " + gamma); System.out.println("Bucketed PV01: " + sensi); System.out.println("PV vega: " + vega.getVega()); System.out.println("Bucketed PV vega:"); System.out.println(" absolute delta: " + volSensi.getDelta()); System.out.println(" time to expiry: " + volSensi.getExpiries()); System.out.println(volSensi.getVega()); } } public void volSurfacePrintTest() { boolean print = false; if (print) { double n = 30.0; double tMax = 15.0; double[] spots = new double[] {STRIKE_AUDUSD, STRIKE_EURUSD, STRIKE_NZDUSD, STRIKE_USDCNH }; Currency[] currencies = new Currency[] {AUD, EUR, NZD, CNH }; BlackForexSmileProviderDiscount[] dscs = new BlackForexSmileProviderDiscount[] {PROVIDER_AUDUSD, PROVIDER_EURUSD, PROVIDER_NZDUSD, PROVIDER_USDCNH }; for (int k = 0; k < 4; ++k) { System.out.println(currencies[k].toString() + "/" + USD.toString()); double kMaxP = spots[k] * 0.8; for (int j = 0; j < n; ++j) { double strike = spots[k] * 0.6 + kMaxP / n * j; System.out.print("\t" + strike); } System.out.print("\n"); for (int i = 0; i < n; ++i) { double time = 1d / 365. + tMax * i / n; double forward = spots[k] * MULTICURVE.getDiscountFactor(currencies[k], time) / MULTICURVE.getDiscountFactor(USD, time); System.out.print(time); for (int j = 0; j < n; ++j) { double strike = spots[k] * 0.6 + kMaxP / n * j; // Note that the USD/CNH is reversed. System.out.print("\t" + dscs[k].getVolatility(currencies[k], USD, time, strike, forward)); } System.out.print("\n"); } System.out.println("\n"); } } } }