public static void annuityFixedDemo(PrintStream out) { double[] paymentTimes = fixedPaymentTimes(MATURITY); AnnuityCouponFixed annuity = new AnnuityCouponFixed(CCY, paymentTimes, R, LIBOR_CURVE_NAME, false); out.println(Arrays.deepToString(annuity.getPayments())); YieldCurveBundle bundle = getBundle(); PresentValueCalculator presentValueCalculator = PresentValueCalculator.getInstance(); double presentValue = presentValueCalculator.visit(annuity, bundle); out.format("Present Value %f%n", presentValue); }
@Test /** Tests put call parity. */ public void presentValuePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition callDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition putDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount pvCall = METHOD_DIGITAL_SPREAD.presentValue(call, SMILE_BUNDLE); final MultipleCurrencyAmount pvPut = METHOD_DIGITAL_SPREAD.presentValue(put, SMILE_BUNDLE); final Double pvCash = PVC.visit(put.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: call spread method - present value", pvCall.getAmount(USD) + pvPut.getAmount(USD), Math.abs(pvCash), TOLERANCE_PRICE_FLAT); }
/** Tests related to the discounting method for bond security. */ public class BondSecurityFRDiscountingMethodTest { // Calculators private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); private static final BondSecurityDiscountingMethod METHOD = BondSecurityDiscountingMethod.getInstance(); private static final double TOLERANCE_PRICE = 1.0E-8; private static final String REPO_TYPE = "General collateral"; private static final Currency EUR = Currency.EUR; private static final Calendar TARGET = new MondayToFridayCalendar("TARGET"); private static final DayCount DAY_COUNT_ACTACTICMA = DayCountFactory.INSTANCE.getDayCount("Actual/Actual ICMA"); private static final BusinessDayConvention BUSINESS_DAY_FOL = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Following"); private static final boolean IS_EOM_FIXED = false; // To derivatives private static final DayCount ACT_ACT = DayCountFactory.INSTANCE.getDayCount("Actual/Actual ISDA"); private static final String CREDIT_CURVE_NAME = "Credit"; private static final String REPO_CURVE_NAME = "Repo"; private static final String FORWARD_CURVE_NAME = "Forward"; private static final String[] CURVES_NAME = { CREDIT_CURVE_NAME, REPO_CURVE_NAME, FORWARD_CURVE_NAME }; private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurvesBond1(); // FRTR 4 1/2 04/25/41 - ISIN: FR0010773192 private static final String ISSUER_FR = "FRANCE (GOVT OF)"; private static final YieldConvention YIELD_CONVENTION_FRANCE = SimpleYieldConvention.FRANCE_COMPOUND_METHOD; private static final int SETTLEMENT_DAYS_FR = 3; private static final Period PAYMENT_TENOR_FR = Period.ofMonths(12); // private static final int COUPON_PER_YEAR_FR = 1; private static final ZonedDateTime BOND_START_FR = DateUtils.getUTCDate(2009, 4, 25); private static final ZonedDateTime BOND_MATURITY_FR = DateUtils.getUTCDate(2041, 4, 25); private static final ZonedDateTime BOND_FIRSTCPN_FR = DateUtils.getUTCDate(2010, 4, 25); private static final double RATE_FR = 0.0450; private static final BondFixedSecurityDefinition BOND_FR_SECURITY_DEFINITION = BondFixedSecurityDefinition.from( EUR, BOND_START_FR, BOND_FIRSTCPN_FR, BOND_MATURITY_FR, PAYMENT_TENOR_FR, RATE_FR, SETTLEMENT_DAYS_FR, TARGET, DAY_COUNT_ACTACTICMA, BUSINESS_DAY_FOL, YIELD_CONVENTION_FRANCE, IS_EOM_FIXED, ISSUER_FR); }
@Test /** Tests the put/call parity currency exposure. */ public void currencyExposurePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinitionCall = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition forexOptionDefinitionPut = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital forexOptionCall = forexOptionDefinitionCall.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital forexOptionPut = forexOptionDefinitionPut.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount currencyExposureCall = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionCall, SMILE_BUNDLE); final MultipleCurrencyAmount currencyExposurePut = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionPut, SMILE_BUNDLE); final Double pvCash = PVC.visit(forexOptionPut.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: currency exposure put/call parity foreign", 0, currencyExposureCall.getAmount(EUR) + currencyExposurePut.getAmount(EUR), TOLERANCE_PRICE); assertEquals( "Forex Digital option: currency exposure put/call parity domestic", Math.abs(pvCash), currencyExposureCall.getAmount(USD) + currencyExposurePut.getAmount(USD), TOLERANCE_PRICE); }
/** Class with methods related to bond transaction valued by discounting. */ public final class BondTransactionDiscountingMethod { private static final Logger LOGGER = LoggerFactory.getLogger(BondTransactionDiscountingMethod.class); /** The unique instance of the class. */ private static final BondTransactionDiscountingMethod INSTANCE = new BondTransactionDiscountingMethod(); /** * Return the class instance. * * @return The instance. */ public static BondTransactionDiscountingMethod getInstance() { return INSTANCE; } /** Constructor */ private BondTransactionDiscountingMethod() {} /** The present value calculator (for the different parts of the bond transaction). */ private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); /** The present value calculator (for the different parts of the bond transaction). */ private static final PresentValueCurveSensitivityCalculator PVSC = PresentValueCurveSensitivityCalculator.getInstance(); private static final BondSecurityDiscountingMethod METHOD_SECURITY = BondSecurityDiscountingMethod.getInstance(); /** * Compute the present value of a fixed coupon bond transaction. * * @param bond The bond transaction. * @param curves The curve bundle. * @return The present value. */ public double presentValue(final BondFixedTransaction bond, final YieldCurveBundle curves) { final double pvNominal = bond.getBondTransaction().getNominal().accept(PVC, curves); final double pvCoupon = bond.getBondTransaction().getCoupon().accept(PVC, curves); final double settlementAmount = -(bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() + bond.getBondTransaction().getAccruedInterest()) * bond.getQuantity(); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final double pvSettlement = settlement.accept(PVC, curves); return (pvNominal + pvCoupon) * bond.getQuantity() + pvSettlement; } /** * Compute the present value of a Ibor coupon bond (FRN) transaction. * * @param bond The bond transaction. * @param curves The curve bundle. * @return The present value. */ public double presentValue(final BondIborTransaction bond, final YieldCurveBundle curves) { final double pvNominal = bond.getBondTransaction().getNominal().accept(PVC, curves); final double pvCoupon = bond.getBondTransaction().getCoupon().accept(PVC, curves); final double settlementAmount = bond.getTransactionPrice() * bond.getBondTransaction() .getCoupon() .getNthPayment(0) .getNotional(); // FIXME: add accrued. LOGGER.error("The FRN settlement amount does not include the accrued interests."); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final double pvSettlement = settlement.accept(PVC, curves); return (pvNominal + pvCoupon) * bond.getQuantity() + pvSettlement; } /** * Compute the present value of a bond transaction from its clean price. * * @param bond The bond transaction. * @param curves The curve bundle. * @param cleanPrice The bond clean price. * @return The present value. */ public double presentValueFromCleanPrice( final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond, final YieldCurveBundle curves, final double cleanPrice) { ArgumentChecker.isTrue( bond instanceof BondFixedTransaction, "Present value from clean price only for fixed coupon bond"); final BondFixedTransaction bondFixed = (BondFixedTransaction) bond; final double dfSettle = curves .getCurve(bondFixed.getBondStandard().getRepoCurveName()) .getDiscountFactor(bondFixed.getBondTransaction().getSettlementTime()); final double pvPriceStandard = (cleanPrice * bondFixed.getNotionalStandard() + bondFixed.getBondStandard().getAccruedInterest()) * dfSettle; final double pvNominalStandard = bond.getBondStandard().getNominal().accept(PVC, curves); final double pvCouponStandard = bond.getBondStandard().getCoupon().accept(PVC, curves); final double pvDiscountingStandard = (pvNominalStandard + pvCouponStandard); final double pvNominalTransaction = bond.getBondTransaction().getNominal().accept(PVC, curves); final double pvCouponTransaction = bond.getBondTransaction().getCoupon().accept(PVC, curves); final double pvDiscountingTransaction = (pvNominalTransaction + pvCouponTransaction); return (pvDiscountingTransaction - pvDiscountingStandard + pvPriceStandard) * bond.getQuantity(); } /** * Compute the present value of a bond transaction from its yield-to-maturity. * * @param bond The bond transaction. * @param curves The curve bundle. * @param yield The bond yield. * @return The present value. */ public double presentValueFromYield( final BondTransaction<? extends BondSecurity<? extends Payment, ? extends Coupon>> bond, final YieldCurveBundle curves, final double yield) { ArgumentChecker.notNull(bond, "Bond"); ArgumentChecker.isTrue( bond instanceof BondFixedTransaction, "Present value from clean price only for fixed coupon bond"); final BondFixedTransaction bondFixed = (BondFixedTransaction) bond; double cleanPrice = METHOD_SECURITY.cleanPriceFromYield(bondFixed.getBondStandard(), yield); return presentValueFromCleanPrice(bond, curves, cleanPrice); } /** * Compute the present value sensitivity of a bond transaction. * * @param bond The bond transaction. * @param curves The curve bundle. * @return The present value sensitivity. */ public InterestRateCurveSensitivity presentValueSensitivity( final BondFixedTransaction bond, final YieldCurveBundle curves) { final InterestRateCurveSensitivity pvsNominal = new InterestRateCurveSensitivity( bond.getBondTransaction().getNominal().accept(PVSC, curves)); final InterestRateCurveSensitivity pvsCoupon = new InterestRateCurveSensitivity( bond.getBondTransaction().getCoupon().accept(PVSC, curves)); final double settlementAmount = -(bond.getTransactionPrice() * bond.getBondTransaction().getCoupon().getNthPayment(0).getNotional() + bond.getBondTransaction().getAccruedInterest()) * bond.getQuantity(); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final InterestRateCurveSensitivity pvsSettlement = new InterestRateCurveSensitivity(settlement.accept(PVSC, curves)); return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement); } public InterestRateCurveSensitivity presentValueSensitivity( final BondIborTransaction bond, final YieldCurveBundle curves) { final InterestRateCurveSensitivity pvsNominal = new InterestRateCurveSensitivity( bond.getBondTransaction().getNominal().accept(PVSC, curves)); final InterestRateCurveSensitivity pvsCoupon = new InterestRateCurveSensitivity( bond.getBondTransaction().getCoupon().accept(PVSC, curves)); final double settlementAmount = bond.getTransactionPrice() * bond.getBondTransaction() .getCoupon() .getNthPayment(0) .getNotional(); // FIXME: add accrued. LOGGER.error("The FRN settlement amount does not include the accrued interests."); final PaymentFixed settlement = new PaymentFixed( bond.getBondTransaction().getCurrency(), bond.getBondTransaction().getSettlementTime(), settlementAmount, bond.getBondTransaction().getRepoCurveName()); final InterestRateCurveSensitivity pvsSettlement = new InterestRateCurveSensitivity(settlement.accept(PVSC, curves)); return pvsNominal.plus(pvsCoupon).multipliedBy(bond.getQuantity()).plus(pvsSettlement); } }
/** Unit tests for USD deals */ @Test(groups = TestGroup.UNIT) public class USDTest extends AbstractMockSourcesTest { private static final Logger s_logger = LoggerFactory.getLogger(USDTest.class); private static final String CURRENCY = "USD"; private static final ZonedDateTime TODAY = DateUtils.getUTCDate(2013, 10, 14); private static final String ON_NAME = "USD_FEDFUNDS_1D_ERS"; private static final String ONE_MONTH_NAME = "USD_LIBOR_1M_ERS"; private static final String THREE_MONTH_NAME = "USD_LIBOR_3M_ERS"; private static final String SIX_MONTH_NAME = "USD_LIBOR_6M_ERS"; static final String discountingCurvename = "Discounting"; static final String forward1MCurveName = "Forward 1M"; static final String forward3MCurveName = "Forward 3M"; static final String forward6MCurveName = "Forward 6M"; static final Currency ccy = Currency.USD; private static final String PAY_CURRENCY = "LEG1_CCY"; private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); public void test() throws Exception { IRSwapTradeParser tradeParser = new IRSwapTradeParser(); Resource resource = ResourceUtils.createResource( "classpath:com/opengamma/financial/analytics/test/Trades14Oct.csv"); List<IRSwapSecurity> trades = tradeParser.parseCSVFile(resource.getURL()); List<IRSwapSecurity> tradesClean = Lists.newArrayList(); for (IRSwapSecurity irSwapSecurity : trades) { String currency = irSwapSecurity.getRawInput().getString(PAY_CURRENCY); if (currency.equals(CURRENCY)) { tradesClean.add(irSwapSecurity); } } // Build the curve bundle final HashMap<String, Currency> ccyMap = new HashMap<>(); ccyMap.put(discountingCurvename, ccy); ccyMap.put(forward3MCurveName, ccy); ccyMap.put(forward6MCurveName, ccy); final FXMatrix fx = new FXMatrix(ccy); final YieldCurveBundle curvesClean = new YieldCurveBundle(fx, ccyMap); IRCurveParser curveParser = new IRCurveParser(); Resource resourceCurve = ResourceUtils.createResource( "classpath:com/opengamma/financial/analytics/test/Base_Curves_20131014_Clean.csv"); List<InterpolatedDoublesCurve> curves = curveParser.parseCSVFile(resourceCurve.getURL()); for (InterpolatedDoublesCurve interpolatedDoublesCurve : curves) { String name = interpolatedDoublesCurve.getName(); if (name.equals(ON_NAME)) { curvesClean.setCurve(discountingCurvename, DiscountCurve.from(interpolatedDoublesCurve)); } if (name.equals(ONE_MONTH_NAME)) { curvesClean.setCurve(forward1MCurveName, DiscountCurve.from(interpolatedDoublesCurve)); } if (name.equals(THREE_MONTH_NAME)) { curvesClean.setCurve(forward3MCurveName, DiscountCurve.from(interpolatedDoublesCurve)); } if (name.equals(SIX_MONTH_NAME)) { curvesClean.setCurve(forward6MCurveName, DiscountCurve.from(interpolatedDoublesCurve)); } } // Convert the swap security into a swap definition final SwapSecurityConverterDeprecated swapConverter = new SwapSecurityConverterDeprecated( _holidaySource, _conventionBundleSource, _regionSource, false); final FRASecurityConverterDeprecated fraConverter = new FRASecurityConverterDeprecated(_holidaySource, _regionSource, _conventionBundleSource); final ZeroDepositConverter ZeroCouponConverter = new ZeroDepositConverter(_conventionBundleSource, _holidaySource); List<SwapDefinition> swapsDefinition = Lists.newArrayList(); List<ForwardRateAgreementDefinition> frasDefinition = Lists.newArrayList(); List<DepositZeroDefinition> zcsDefinition = Lists.newArrayList(); /*for (IRSwapSecurity irSwapSecurity : tradesClean) { switch (irSwapSecurity.getRawInput().getString("PRODUCT_TYPE")) { case "SWAP": swapsDefinition.add((SwapDefinition) swapConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity())); // we don't treat the fra case at the moment case "FRA": frasDefinition.add((ForwardRateAgreementDefinition) fraConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity())); case "OIS": swapsDefinition.add((SwapDefinition) swapConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity())); // we don't treat the fra case at the moment case "ZCS": zcsDefinition.add((DepositZeroDefinition) ZeroCouponConverter.visitSwapSecurity(irSwapSecurity.getSwapSecurity())); } } */ // Load the historical time series from a csv file /* NonVersionedRedisHistoricalTimeSeriesSource source = new NonVersionedRedisHistoricalTimeSeriesSource(getJedisPool(), getRedisPrefix()); CMECurveFixingTSLoader loader = new CMECurveFixingTSLoader(source);*/ /* loader.loadCurveFixingCSVFile("/vols/ogdev/CME/curve-fixing/sample-cme-curve-fixing.csv"); HistoricalTimeSeries historicalTimeSeries = source.getHistoricalTimeSeries(UniqueId.of(ExternalSchemes.ISDA.getName(), "CHF-LIBOR-BBA-6M")); assertNotNull(historicalTimeSeries); LocalDateDoubleTimeSeries timeSeries = historicalTimeSeries.getTimeSeries(); assertNotNull(timeSeries); assertEquals(5996, timeSeries.size());*/ // convert the definition into a derivative /* List<Swap> swapDerivatives = Lists.newArrayList(); for (SwapDefinition swapDefinition : swapsDefinition) { swapDerivatives.add(swapDefinition.toDerivative(TODAY, data, curvesClean)); } List<Swap> frasDerivatives = Lists.newArrayList(); for (ForwardRateAgreementDefinition fraDefinition : frasDefinition) { frasDerivatives.add(fraDefinition.toDerivative(TODAY, data, curvesClean)); } List<Swap> zcsDerivatives = Lists.newArrayList(); for (DepositZeroDefinition zcDefinition : zcsDefinition) { zcsDerivatives.add(zcDefinition.toDerivative(TODAY, data, curvesClean)); }*/ // Check the npv s_logger.warn("Got {} trades", trades.size()); } }
@Override protected InstrumentDerivativeVisitorAdapter<YieldCurveBundle, Double> getCalculator() { return PresentValueCalculator.getInstance(); }
/** * Test related to the pricing and sensitivity of the Ibor cap/floor with the SABR model and * extrapolation for high strikes. * * @deprecated This class tests deprecated functionality. */ @Deprecated public class CapFloorIborSABRExtrapolationRightMethodTest { // Details private static final Period TENOR = Period.ofMonths(3); private static final int SETTLEMENT_DAYS = 2; private static final Calendar CALENDAR = new MondayToFridayCalendar("A"); private static final DayCount DAY_COUNT_INDEX = DayCountFactory.INSTANCE.getDayCount("Actual/360"); private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following"); private static final boolean IS_EOM = true; private static final Currency CUR = Currency.EUR; private static final IborIndex INDEX = new IborIndex(CUR, TENOR, SETTLEMENT_DAYS, DAY_COUNT_INDEX, BUSINESS_DAY, IS_EOM, "Ibor"); private static final ZonedDateTime FIXING_DATE = DateUtils.getUTCDate(2011, 1, 3); private static final double NOTIONAL = 1000000; // 1m private static final double STRIKE = 0.04; private static final double STRIKE_HIGH = 0.09; private static final boolean IS_CAP = true; // Definition description private static final CapFloorIborDefinition CAP_LONG_DEFINITION = CapFloorIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR); private static final CapFloorIborDefinition CAP_HIGH_LONG_DEFINITION = CapFloorIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, IS_CAP, CALENDAR); private static final CouponIborDefinition COUPON_IBOR_DEFINITION = CouponIborDefinition.from(NOTIONAL, FIXING_DATE, INDEX, CALENDAR); private static final CouponFixedDefinition COUPON_STRIKE_DEFINITION = new CouponFixedDefinition(COUPON_IBOR_DEFINITION, STRIKE); private static final CouponFixedDefinition COUPON_STRIKE_HIGH_DEFINITION = new CouponFixedDefinition(COUPON_IBOR_DEFINITION, STRIKE_HIGH); private static final CapFloorIborDefinition CAP_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, IS_CAP, CALENDAR); private static final CapFloorIborDefinition CAP_HIGH_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, IS_CAP, CALENDAR); private static final CapFloorIborDefinition FLOOR_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE, !IS_CAP, CALENDAR); private static final CapFloorIborDefinition FLOOR_HIGH_SHORT_DEFINITION = CapFloorIborDefinition.from(-NOTIONAL, FIXING_DATE, INDEX, STRIKE_HIGH, !IS_CAP, CALENDAR); // Methods and calculator private static final double CUT_OFF_STRIKE = 0.08; private static final double MU = 2.50; private static final CapFloorIborSABRExtrapolationRightMethod METHOD = new CapFloorIborSABRExtrapolationRightMethod(CUT_OFF_STRIKE, MU); private static final ParRateCalculator PRC = ParRateCalculator.getInstance(); private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); private static final PresentValueCurveSensitivitySABRExtrapolationCalculator PVSC = new PresentValueCurveSensitivitySABRExtrapolationCalculator(CUT_OFF_STRIKE, MU); // To derivative private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2008, 8, 18); private static final String FUNDING_CURVE_NAME = "Funding"; private static final String FORWARD_CURVE_NAME = "Forward"; private static final String[] CURVES_NAME = {FUNDING_CURVE_NAME, FORWARD_CURVE_NAME}; private static final CapFloorIbor CAP_LONG = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CapFloorIbor CAP_HIGH_LONG = (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CouponIbor COUPON_IBOR = (CouponIbor) COUPON_IBOR_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CouponFixed COUPON_STRIKE = COUPON_STRIKE_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CouponFixed COUPON_STRIKE_HIGH = COUPON_STRIKE_HIGH_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CapFloorIbor CAP_SHORT = (CapFloorIbor) CAP_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CapFloorIbor CAP_HIGH_SHORT = (CapFloorIbor) CAP_HIGH_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CapFloorIbor FLOOR_SHORT = (CapFloorIbor) FLOOR_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final CapFloorIbor FLOOR_HIGH_SHORT = (CapFloorIbor) FLOOR_HIGH_SHORT_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); // Data private static final YieldCurveBundle CURVES = TestsDataSetsSABR.createCurves1(); private static final SABRInterestRateParameters SABR_PARAMETERS = TestsDataSetsSABR.createSABR1(); private static final SABRInterestRateDataBundle SABR_BUNDLE = new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES); @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueBelowCutOff() { final CurrencyAmount methodPrice = METHOD.presentValue(CAP_LONG, SABR_BUNDLE); final double df = CURVES.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_LONG.getPaymentTime()); final double forward = CAP_LONG.accept(PRC, CURVES); final double maturity = CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime(); final DoublesPair expiryMaturity = new DoublesPair(CAP_LONG.getFixingTime(), maturity); final double alpha = SABR_PARAMETERS.getAlpha(expiryMaturity); final double beta = SABR_PARAMETERS.getBeta(expiryMaturity); final double rho = SABR_PARAMETERS.getRho(expiryMaturity); final double nu = SABR_PARAMETERS.getNu(expiryMaturity); final SABRFormulaData sabrParam = new SABRFormulaData(alpha, beta, rho, nu); final SABRExtrapolationRightFunction sabrExtrapolation = new SABRExtrapolationRightFunction( forward, sabrParam, CUT_OFF_STRIKE, CAP_LONG.getFixingTime(), MU); final EuropeanVanillaOption option = new EuropeanVanillaOption(CAP_LONG.getStrike(), CAP_LONG.getFixingTime(), CAP_LONG.isCap()); final double expectedPrice = sabrExtrapolation.price(option) * CAP_LONG.getNotional() * CAP_LONG.getPaymentYearFraction() * df; assertEquals( "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2); } @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueAboveCutOff() { CurrencyAmount methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); final double df = CURVES.getCurve(FUNDING_CURVE_NAME).getDiscountFactor(CAP_HIGH_LONG.getPaymentTime()); final double forward = CAP_HIGH_LONG.accept(PRC, CURVES); final double maturity = CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime(); final DoublesPair expiryMaturity = new DoublesPair(CAP_HIGH_LONG.getFixingTime(), maturity); final double alpha = SABR_PARAMETERS.getAlpha(expiryMaturity); final double beta = SABR_PARAMETERS.getBeta(expiryMaturity); final double rho = SABR_PARAMETERS.getRho(expiryMaturity); final double nu = SABR_PARAMETERS.getNu(expiryMaturity); final SABRFormulaData sabrParam = new SABRFormulaData(alpha, beta, rho, nu); final SABRExtrapolationRightFunction sabrExtrapolation = new SABRExtrapolationRightFunction( forward, sabrParam, CUT_OFF_STRIKE, CAP_HIGH_LONG.getFixingTime(), MU); final EuropeanVanillaOption option = new EuropeanVanillaOption( CAP_HIGH_LONG.getStrike(), CAP_HIGH_LONG.getFixingTime(), CAP_HIGH_LONG.isCap()); final double expectedPrice = sabrExtrapolation.price(option) * CAP_HIGH_LONG.getNotional() * CAP_HIGH_LONG.getPaymentYearFraction() * df; assertEquals( "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2); methodPrice = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); assertEquals( "Cap/floor: SABR with extrapolation pricing", expectedPrice, methodPrice.getAmount(), 1E-2); } @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueLongShortParityBelowCutOff() { final CurrencyAmount priceLong = METHOD.presentValue(CAP_LONG, SABR_BUNDLE); final CurrencyAmount priceShort = METHOD.presentValue(CAP_SHORT, SABR_BUNDLE); assertEquals( "Cap/floor: SABR with extrapolation pricing: long/short parity", priceLong.getAmount(), -priceShort.getAmount(), 1E-2); } @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueLongShortParityAboveCutOff() { final CurrencyAmount priceLong = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); final CurrencyAmount priceShort = METHOD.presentValue(CAP_HIGH_SHORT, SABR_BUNDLE); assertEquals( "Cap/floor: SABR with extrapolation pricing: long/short parity", priceLong.getAmount(), -priceShort.getAmount(), 1E-2); } @Test /** Test the cap/floor/forward parity below the cut-off strike. */ public void presentValueCapFloorParityBelowCutOff() { final CurrencyAmount priceCap = METHOD.presentValue(CAP_LONG, SABR_BUNDLE); final CurrencyAmount priceFloor = METHOD.presentValue(FLOOR_SHORT, SABR_BUNDLE); final double priceCouponStrike = COUPON_STRIKE.accept(PVC, CURVES); final double priceCouponIbor = COUPON_IBOR.accept(PVC, CURVES); assertEquals( "Cap/floor: SABR with extrapolation pricing: cap/floor parity", priceCouponIbor - priceCouponStrike, priceCap.getAmount() + priceFloor.getAmount(), 1E-2); } @Test /** Test the cap/floor/forward parity above the cut-off strike. */ public void presentValueCapFloorParityAboveCutOff() { final CurrencyAmount priceCap = METHOD.presentValue(CAP_HIGH_LONG, SABR_BUNDLE); final CurrencyAmount priceFloor = METHOD.presentValue(FLOOR_HIGH_SHORT, SABR_BUNDLE); final double priceCouponStrike = COUPON_STRIKE_HIGH.accept(PVC, CURVES); final double priceCouponIbor = COUPON_IBOR.accept(PVC, CURVES); assertEquals( "Cap/floor: SABR with extrapolation pricing: cap/floor parity", priceCouponIbor - priceCouponStrike, priceCap.getAmount() + priceFloor.getAmount(), 1E-2); } @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueMethodVsCalculator() { final SABRInterestRateDataBundle sabrExtraBundle = new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES); final CurrencyAmount pvMethod = METHOD.presentValue(CAP_LONG, SABR_BUNDLE); final PresentValueSABRExtrapolationCalculator pvc = new PresentValueSABRExtrapolationCalculator(CUT_OFF_STRIKE, MU); final double pvCalculator = CAP_LONG.accept(pvc, sabrExtraBundle); assertEquals( "Cap/floor: SABR with extrapolation pricing - Method vs Calculator", pvMethod.getAmount(), pvCalculator, 1E-2); } @Test /** * Test the present value rate sensitivity against a finite difference computation; strike below * the cut-off strike. Test sensitivity long/short parity. */ public void testPresentValueSensitivityBelowCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); InterestRateCurveSensitivity pvsCapLong = METHOD.presentValueSensitivity(CAP_LONG, sabrBundle); final InterestRateCurveSensitivity pvsCapShort = METHOD.presentValueSensitivity(CAP_SHORT, sabrBundle); // Long/short parity final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1); assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities()); // Present value sensitivity comparison with finite difference. final double deltaTolerancePrice = 1.0E-1; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. final double deltaShift = 1.0E-7; pvsCapLong = pvsCapLong.cleaned(); final String bumpedCurveName = "Bumped Curve"; // 1. Forward curve sensitivity final String[] CurveNameBumpedForward = {FUNDING_CURVE_NAME, bumpedCurveName}; final CapFloorIbor capBumpedForward = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedForward); final double[] nodeTimesForward = new double[] { capBumpedForward.getFixingPeriodStartTime(), capBumpedForward.getFixingPeriodEndTime() }; final double[] sensiForwardMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedForward, SABR_BUNDLE, FORWARD_CURVE_NAME, bumpedCurveName, nodeTimesForward, deltaShift, METHOD); assertEquals( "Sensitivity finite difference method: number of node", 2, sensiForwardMethod.length); final List<DoublesPair> sensiPvForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME); for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvForward.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesForward[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", pairPv.second, sensiForwardMethod[loopnode], deltaTolerancePrice); } // 2. Discounting curve sensitivity final String[] CurveNameBumpedDisc = {bumpedCurveName, FORWARD_CURVE_NAME}; final CapFloorIbor capBumpedDisc = (CapFloorIbor) CAP_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedDisc); final double[] nodeTimesDisc = new double[] {capBumpedDisc.getPaymentTime()}; final double[] sensiDiscMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedDisc, SABR_BUNDLE, FUNDING_CURVE_NAME, bumpedCurveName, nodeTimesDisc, deltaShift, METHOD); assertEquals("Sensitivity finite difference method: number of node", 1, sensiDiscMethod.length); final List<DoublesPair> sensiPvDisc = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME); for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvDisc.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesDisc[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", pairPv.second, sensiDiscMethod[loopnode], deltaTolerancePrice); } } @Test /** * Test the present value rate sensitivity against a finite difference computation; strike above * the cut-off strike. Test sensitivity long/short parity. */ public void testPresentValueSensitivityAboveCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); InterestRateCurveSensitivity pvsCapLong = METHOD.presentValueSensitivity(CAP_HIGH_LONG, sabrBundle); final InterestRateCurveSensitivity pvsCapShort = METHOD.presentValueSensitivity(CAP_HIGH_SHORT, sabrBundle); // Long/short parity final InterestRateCurveSensitivity pvsCapShort_1 = pvsCapShort.multipliedBy(-1); assertEquals(pvsCapLong.getSensitivities(), pvsCapShort_1.getSensitivities()); // Present value sensitivity comparison with finite difference. final double deltaTolerancePrice = 1.0E-1; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 1 cent for a 1 bp move. final double deltaShift = 1.0E-7; pvsCapLong = pvsCapLong.cleaned(); final String bumpedCurveName = "Bumped Curve"; // 1. Forward curve sensitivity final String[] CurveNameBumpedForward = {FUNDING_CURVE_NAME, bumpedCurveName}; final CapFloorIbor capBumpedForward = (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedForward); final double[] nodeTimesForward = new double[] { capBumpedForward.getFixingPeriodStartTime(), capBumpedForward.getFixingPeriodEndTime() }; final double[] sensiForwardMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedForward, SABR_BUNDLE, FORWARD_CURVE_NAME, bumpedCurveName, nodeTimesForward, deltaShift, METHOD); assertEquals( "Sensitivity finite difference method: number of node", 2, sensiForwardMethod.length); final List<DoublesPair> sensiPvForward = pvsCapLong.getSensitivities().get(FORWARD_CURVE_NAME); for (int loopnode = 0; loopnode < sensiForwardMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvForward.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesForward[loopnode], pairPv.getFirst(), 1E-8); // assertEquals("Sensitivity finite difference method: node sensitivity: Node " + // loopnode, pairPv.second, sensiForwardMethod[loopnode], deltaTolerancePrice); } // 2. Discounting curve sensitivity final String[] CurveNameBumpedDisc = {bumpedCurveName, FORWARD_CURVE_NAME}; final CapFloorIbor capBumpedDisc = (CapFloorIbor) CAP_HIGH_LONG_DEFINITION.toDerivative(REFERENCE_DATE, CurveNameBumpedDisc); final double[] nodeTimesDisc = new double[] {capBumpedDisc.getPaymentTime()}; final double[] sensiDiscMethod = SensitivityFiniteDifference.curveSensitivity( capBumpedDisc, SABR_BUNDLE, FUNDING_CURVE_NAME, bumpedCurveName, nodeTimesDisc, deltaShift, METHOD); assertEquals("Sensitivity finite difference method: number of node", 1, sensiDiscMethod.length); final List<DoublesPair> sensiPvDisc = pvsCapLong.getSensitivities().get(FUNDING_CURVE_NAME); for (int loopnode = 0; loopnode < sensiDiscMethod.length; loopnode++) { final DoublesPair pairPv = sensiPvDisc.get(loopnode); assertEquals( "Sensitivity cap/floor pv to forward curve: Node " + loopnode, nodeTimesDisc[loopnode], pairPv.getFirst(), 1E-8); assertEquals( "Sensitivity finite difference method: node sensitivity", pairPv.second, sensiDiscMethod[loopnode], deltaTolerancePrice); } } @Test /** Test the present value using the method with the direct formula with extrapolation. */ public void presentValueCurveSensitivityMethodVsCalculator() { final SABRInterestRateDataBundle sabrExtraBundle = new SABRInterestRateDataBundle(SABR_PARAMETERS, CURVES); final InterestRateCurveSensitivity pvsMethod = METHOD.presentValueSensitivity(CAP_HIGH_LONG, SABR_BUNDLE); final InterestRateCurveSensitivity pvsCalculator = new InterestRateCurveSensitivity(CAP_HIGH_LONG.accept(PVSC, sabrExtraBundle)); assertEquals( "Cap/floor: SABR with extrapolation pv curve sensitivity - Method vs Calculator", pvsMethod, pvsCalculator); } @Test /** * Test the present value SABR parameters sensitivity against a finite difference computation; * strike below the cut-off strike. */ public void testPresentValueSABRSensitivityBelowCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); final CurrencyAmount pv = METHOD.presentValue(CAP_LONG, sabrBundle); final PresentValueSABRSensitivityDataBundle pvsCapLong = METHOD.presentValueSABRSensitivity(CAP_LONG, sabrBundle); PresentValueSABRSensitivityDataBundle pvsCapShort = METHOD.presentValueSABRSensitivity(CAP_SHORT, sabrBundle); // Long/short parity pvsCapShort = pvsCapShort.multiplyBy(-1.0); assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha()); // SABR sensitivity vs finite difference final double shift = 0.0001; final double shiftAlpha = 0.00001; final DoublesPair expectedExpiryTenor = new DoublesPair( CAP_LONG.getFixingTime(), CAP_LONG.getFixingPeriodEndTime() - CAP_LONG.getFixingPeriodStartTime()); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterAlphaBumped = TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha); final SABRInterestRateDataBundle sabrBundleAlphaBumped = new SABRInterestRateDataBundle(sabrParameterAlphaBumped, curves); final CurrencyAmount pvLongPayerAlphaBumped = METHOD.presentValue(CAP_LONG, sabrBundleAlphaBumped); final double expectedAlphaSensi = (pvLongPayerAlphaBumped.getAmount() - pv.getAmount()) / shiftAlpha; assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1); assertEquals( "Alpha sensitivity expiry/tenor", pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Alpha sensitivity value", expectedAlphaSensi, pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor), 2.0E-1); // Rho sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterRhoBumped = TestsDataSetsSABR.createSABR1RhoBumped(); final SABRInterestRateDataBundle sabrBundleRhoBumped = new SABRInterestRateDataBundle(sabrParameterRhoBumped, curves); final CurrencyAmount pvLongPayerRhoBumped = METHOD.presentValue(CAP_LONG, sabrBundleRhoBumped); final double expectedRhoSensi = (pvLongPayerRhoBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1); assertEquals( "Rho sensitivity expiry/tenor", pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Rho sensitivity value", pvsCapLong.getRho().getMap().get(expectedExpiryTenor), expectedRhoSensi, 1.0E-2); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterNuBumped = TestsDataSetsSABR.createSABR1NuBumped(); final SABRInterestRateDataBundle sabrBundleNuBumped = new SABRInterestRateDataBundle(sabrParameterNuBumped, curves); final CurrencyAmount pvLongPayerNuBumped = METHOD.presentValue(CAP_LONG, sabrBundleNuBumped); final double expectedNuSensi = (pvLongPayerNuBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1); assertEquals( "Nu sensitivity expiry/tenor", pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Nu sensitivity value", pvsCapLong.getNu().getMap().get(expectedExpiryTenor), expectedNuSensi, 3.0E-2); } @Test /** * Test the present value SABR parameters sensitivity against a finite difference computation; * strike above the cut-off strike. */ public void testPresentValueSABRSensitivityAboveCutOff() { final YieldCurveBundle curves = TestsDataSetsSABR.createCurves1(); final SABRInterestRateParameters sabrParameter = TestsDataSetsSABR.createSABR1(); final SABRInterestRateDataBundle sabrBundle = new SABRInterestRateDataBundle(sabrParameter, curves); final CurrencyAmount pv = METHOD.presentValue(CAP_HIGH_LONG, sabrBundle); final PresentValueSABRSensitivityDataBundle pvsCapLong = METHOD.presentValueSABRSensitivity(CAP_HIGH_LONG, sabrBundle); PresentValueSABRSensitivityDataBundle pvsCapShort = METHOD.presentValueSABRSensitivity(CAP_HIGH_SHORT, sabrBundle); // Long/short parity pvsCapShort = pvsCapShort.multiplyBy(-1.0); assertEquals(pvsCapShort.getAlpha(), pvsCapLong.getAlpha()); // SABR sensitivity vs finite difference final double shift = 0.0001; final double shiftAlpha = 0.00001; final DoublesPair expectedExpiryTenor = new DoublesPair( CAP_HIGH_LONG.getFixingTime(), CAP_HIGH_LONG.getFixingPeriodEndTime() - CAP_HIGH_LONG.getFixingPeriodStartTime()); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterAlphaBumped = TestsDataSetsSABR.createSABR1AlphaBumped(shiftAlpha); final SABRInterestRateDataBundle sabrBundleAlphaBumped = new SABRInterestRateDataBundle(sabrParameterAlphaBumped, curves); final CurrencyAmount pvLongPayerAlphaBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleAlphaBumped); final double expectedAlphaSensi = (pvLongPayerAlphaBumped.getAmount() - pv.getAmount()) / shiftAlpha; assertEquals("Number of alpha sensitivity", pvsCapLong.getAlpha().getMap().keySet().size(), 1); assertEquals( "Alpha sensitivity expiry/tenor", pvsCapLong.getAlpha().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Alpha sensitivity value", expectedAlphaSensi, pvsCapLong.getAlpha().getMap().get(expectedExpiryTenor), 1.0E-0); // Rho sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterRhoBumped = TestsDataSetsSABR.createSABR1RhoBumped(); final SABRInterestRateDataBundle sabrBundleRhoBumped = new SABRInterestRateDataBundle(sabrParameterRhoBumped, curves); final CurrencyAmount pvLongPayerRhoBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleRhoBumped); final double expectedRhoSensi = (pvLongPayerRhoBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of rho sensitivity", pvsCapLong.getRho().getMap().keySet().size(), 1); assertEquals( "Rho sensitivity expiry/tenor", pvsCapLong.getRho().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Rho sensitivity value", pvsCapLong.getRho().getMap().get(expectedExpiryTenor), expectedRhoSensi, 1.0E-1); // Alpha sensitivity vs finite difference computation final SABRInterestRateParameters sabrParameterNuBumped = TestsDataSetsSABR.createSABR1NuBumped(); final SABRInterestRateDataBundle sabrBundleNuBumped = new SABRInterestRateDataBundle(sabrParameterNuBumped, curves); final CurrencyAmount pvLongPayerNuBumped = METHOD.presentValue(CAP_HIGH_LONG, sabrBundleNuBumped); final double expectedNuSensi = (pvLongPayerNuBumped.getAmount() - pv.getAmount()) / shift; assertEquals("Number of nu sensitivity", pvsCapLong.getNu().getMap().keySet().size(), 1); assertEquals( "Nu sensitivity expiry/tenor", pvsCapLong.getNu().getMap().keySet().contains(expectedExpiryTenor), true); assertEquals( "Nu sensitivity value", pvsCapLong.getNu().getMap().get(expectedExpiryTenor), expectedNuSensi, 2.0E-1); } }
/** * Tests related to the pricing of bills security by discounting. * * @deprecated This class tests deprecated functionality */ @Deprecated public class BillSecurityDiscountingMethodTest { private static final Currency EUR = Currency.EUR; private static final Calendar CALENDAR = new MondayToFridayCalendar("TARGET"); private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2012, 1, 17); private static final DayCount ACT360 = DayCountFactory.INSTANCE.getDayCount("Actual/360"); private static final int SETTLEMENT_DAYS = 2; private static final YieldConvention YIELD_IAM = YieldConventionFactory.INSTANCE.getYieldConvention("INTEREST@MTY"); private static final YieldConvention YIELD_DSC = YieldConventionFactory.INSTANCE.getYieldConvention("DISCOUNT"); // ISIN: BE0312677462 private static final String ISSUER_BEL = "BELGIUM GOVT"; private static final String ISSUER_US = "US GOVT"; private static final ZonedDateTime END_DATE = DateUtils.getUTCDate(2012, 3, 15); private static final double NOTIONAL = 1000; private static final double YIELD = 0.00185; // External source private static final double PRICE = 0.99971; // External source private static final ZonedDateTime SETTLE_DATE = ScheduleCalculator.getAdjustedDate(REFERENCE_DATE, SETTLEMENT_DAYS, CALENDAR); private static final String[] NAME_CURVES = TestsDataSetsSABR.nameCurvesBond3(); private static final BillSecurityDefinition BILL_IAM_SEC_DEFINITION = new BillSecurityDefinition( EUR, END_DATE, NOTIONAL, SETTLEMENT_DAYS, CALENDAR, YIELD_IAM, ACT360, ISSUER_BEL); private static final BillSecurityDefinition BILL_DSC_SEC_DEFINITION = new BillSecurityDefinition( EUR, END_DATE, NOTIONAL, SETTLEMENT_DAYS, CALENDAR, YIELD_DSC, ACT360, ISSUER_US); // Should not be in EUR, but this is only a test private static final BillSecurity BILL_IAM_SEC = BILL_IAM_SEC_DEFINITION.toDerivative(REFERENCE_DATE, SETTLE_DATE, NAME_CURVES); private static final BillSecurity BILL_DSC_SEC = BILL_DSC_SEC_DEFINITION.toDerivative(REFERENCE_DATE, SETTLE_DATE, NAME_CURVES); private static final YieldCurveBundle CURVE_BUNDLE = TestsDataSetsSABR.createCurvesBond3(); private static final BillSecurityDiscountingMethod METHOD_SECURITY = BillSecurityDiscountingMethod.getInstance(); private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); private static final PresentValueCurveSensitivityCalculator PVCSC = PresentValueCurveSensitivityCalculator.getInstance(); private static final YieldFromCurvesCalculator YFCC = YieldFromCurvesCalculator.getInstance(); private static final YieldFromPriceCalculator YFPC = YieldFromPriceCalculator.getInstance(); private static final double TOLERANCE_PV = 1.0E-2; private static final double TOLERANCE_PRICE = 1.0E-8; private static final double TOLERANCE_PRICE_EXTERNAL = 1.0E-5; private static final double TOLERANCE_YIELD = 1.0E-8; private static final double TOLERANCE_YIELD_EXTERNAL = 1.0E-4; private static final double TOLERANCE_YIELD_DERIVATIVE = 1.0E-6; @Test /** Tests the present value against explicit computation. */ public void presentValue() { final CurrencyAmount pvComputed = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE); final double pvExpected = NOTIONAL * CURVE_BUNDLE.getCurve(NAME_CURVES[1]).getDiscountFactor(BILL_IAM_SEC.getEndTime()); assertEquals( "Bill Security: discounting method - present value", pvExpected, pvComputed.getAmount(), TOLERANCE_PV); } @Test /** Tests the present value: Method vs Calculator */ public void presentValueMethodVsCalculator() { final CurrencyAmount pvMethod = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE); final double pvCalculator = BILL_IAM_SEC.accept(PVC, CURVE_BUNDLE); assertEquals( "Bill Security: discounting method - present value", pvMethod.getAmount(), pvCalculator, TOLERANCE_PV); } @Test public void priceFromYield() { final double[] yields = new double[] {0.0010, 0.0, -0.0010}; for (final double yield2 : yields) { final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, yield2); final double priceExpected = 1.0 / (1 + BILL_IAM_SEC.getAccrualFactor() * yield2); assertEquals( "Bill Security: discounting method - price", priceExpected, priceComputed, TOLERANCE_PRICE); } } @Test public void priceFromYieldExternal() { final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD); assertEquals( "Bill Security: discounting method - price", PRICE, priceComputed, TOLERANCE_PRICE_EXTERNAL); } @Test public void yieldFromPrice() { final double yieldComputedIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE); final double yieldExpectedIAM = (1.0 / PRICE - 1.0) / BILL_IAM_SEC.getAccrualFactor(); assertEquals( "Bill Security: discounting method - yield", yieldExpectedIAM, yieldComputedIAM, TOLERANCE_YIELD); final double yieldComputedDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE); final double yieldExpectedDSC = (1.0 - PRICE) / BILL_DSC_SEC.getAccrualFactor(); assertEquals( "Bill Security: discounting method - yield", yieldExpectedDSC, yieldComputedDSC, TOLERANCE_YIELD); } @Test public void yieldFromPriceDerivative() { final double shift = 1.0E-8; final double yieldIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE); final double yieldPIAM = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE + shift); final double yieldDerivativeExpectedIAM = (yieldPIAM - yieldIAM) / shift; final double yieldDerivativeComputedIAM = METHOD_SECURITY.yieldFromPriceDerivative(BILL_IAM_SEC, PRICE); assertEquals( "Bill Security: discounting method - yield", yieldDerivativeExpectedIAM, yieldDerivativeComputedIAM, TOLERANCE_YIELD_DERIVATIVE); final double yieldDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE); final double yieldPDSC = METHOD_SECURITY.yieldFromPrice(BILL_DSC_SEC, PRICE + shift); final double yieldDerivativeExpectedDSC = (yieldPDSC - yieldDSC) / shift; final double yieldDerivativeComputedDSC = METHOD_SECURITY.yieldFromPriceDerivative(BILL_DSC_SEC, PRICE); assertEquals( "Bill Security: discounting method - yield", yieldDerivativeExpectedDSC, yieldDerivativeComputedDSC, TOLERANCE_YIELD_DERIVATIVE); } @Test public void yieldFromPriceExternal() { final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE); assertEquals( "Bill Security: discounting method - yield", YIELD, yieldComputed, TOLERANCE_YIELD_EXTERNAL); } @Test public void yieldFromPriceCoherence() { final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD); final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, priceComputed); assertEquals( "Bill Security: discounting method - yield", YIELD, yieldComputed, TOLERANCE_YIELD); } @Test public void priceFromYieldCoherence() { final double yieldComputed = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE); final double priceComputed = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, yieldComputed); assertEquals( "Bill Security: discounting method - price", PRICE, priceComputed, TOLERANCE_PRICE); } @Test public void presentValueFromPrice() { final CurrencyAmount pvComputed = METHOD_SECURITY.presentValueFromPrice(BILL_IAM_SEC, PRICE, CURVE_BUNDLE); final double pvExpected = NOTIONAL * PRICE * CURVE_BUNDLE .getCurve(NAME_CURVES[0]) .getDiscountFactor(BILL_IAM_SEC.getSettlementTime()); assertEquals( "Bill Security: discounting method - present value", pvExpected, pvComputed.getAmount(), TOLERANCE_PV); } @Test public void presentValueFromYield() { final CurrencyAmount pvComputed = METHOD_SECURITY.presentValueFromYield(BILL_IAM_SEC, YIELD, CURVE_BUNDLE); final double price = METHOD_SECURITY.priceFromYield(BILL_IAM_SEC, YIELD); final double pvExpected = NOTIONAL * price * CURVE_BUNDLE .getCurve(NAME_CURVES[0]) .getDiscountFactor(BILL_IAM_SEC.getSettlementTime()); assertEquals( "Bill Security: discounting method - present value", pvExpected, pvComputed.getAmount(), TOLERANCE_PV); } @Test public void priceFromCurves() { final double priceComputed = METHOD_SECURITY.priceFromCurves(BILL_IAM_SEC, CURVE_BUNDLE); final CurrencyAmount pvComputed = METHOD_SECURITY.presentValue(BILL_IAM_SEC, CURVE_BUNDLE); final double priceExpected = pvComputed.getAmount() / (NOTIONAL * CURVE_BUNDLE .getCurve(NAME_CURVES[0]) .getDiscountFactor(BILL_IAM_SEC.getSettlementTime())); assertEquals( "Bill Security: discounting method - price", priceExpected, priceComputed, TOLERANCE_PRICE); } @Test public void yieldFromCurves() { final double yieldComputed = METHOD_SECURITY.yieldFromCurves(BILL_IAM_SEC, CURVE_BUNDLE); final double priceComputed = METHOD_SECURITY.priceFromCurves(BILL_IAM_SEC, CURVE_BUNDLE); final double yieldExpected = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, priceComputed); assertEquals( "Bill Security: discounting method - yield", yieldExpected, yieldComputed, TOLERANCE_YIELD); } @Test public void presentValueCurveSensitivity() { final InterestRateCurveSensitivity pvcsComputed = METHOD_SECURITY.presentValueCurveSensitivity(BILL_IAM_SEC, CURVE_BUNDLE); assertEquals( "Bill Security: present value curve sensitivity", 1, pvcsComputed.getSensitivities().size()); assertEquals( "Bill Security: present value curve sensitivity", 1, pvcsComputed.getSensitivities().get(NAME_CURVES[1]).size()); final double deltaTolerancePrice = 1.0E+2; // Testing note: Sensitivity is for a movement of 1. 1E+2 = 0.01 unit for a 1 bp move. final double deltaShift = 1.0E-6; // Credit curve sensitivity final String bumpedCurveName = "Bumped Curve"; final BillSecurity billBumped = BILL_IAM_SEC_DEFINITION.toDerivative(REFERENCE_DATE, NAME_CURVES[0], bumpedCurveName); final double[] nodeTimes = new double[] {billBumped.getEndTime()}; final double[] sensi = SensitivityFiniteDifference.curveSensitivity( billBumped, CURVE_BUNDLE, NAME_CURVES[1], bumpedCurveName, nodeTimes, deltaShift, METHOD_SECURITY); final List<DoublesPair> sensiPv = pvcsComputed.getSensitivities().get(NAME_CURVES[1]); for (int loopnode = 0; loopnode < sensi.length; loopnode++) { final DoublesPair pairPv = sensiPv.get(loopnode); assertEquals( "Bill Security: curve sensitivity - Node " + loopnode, nodeTimes[loopnode], pairPv.getFirst(), 1E-8); AssertJUnit.assertEquals( "Bill Security: curve sensitivity", pairPv.second, sensi[loopnode], deltaTolerancePrice); } } @Test public void presentValueCurveSensitivityMethodVsCalculator() { final InterestRateCurveSensitivity pvcsMethod = METHOD_SECURITY.presentValueCurveSensitivity(BILL_IAM_SEC, CURVE_BUNDLE); final InterestRateCurveSensitivity pvcsCalculator = new InterestRateCurveSensitivity(BILL_IAM_SEC.accept(PVCSC, CURVE_BUNDLE)); AssertSensivityObjects.assertEquals( "Bill Security: discounting method - curve sensitivity", pvcsMethod, pvcsCalculator, TOLERANCE_PV); } @Test public void methodVsCalculator() { double yield1 = METHOD_SECURITY.yieldFromCurves(BILL_IAM_SEC, CURVE_BUNDLE); double yield2 = BILL_IAM_SEC.accept(YFCC, CURVE_BUNDLE); assertEquals( "Bill Security: discounting method - yield from curves", yield1, yield2, TOLERANCE_YIELD); yield1 = METHOD_SECURITY.yieldFromPrice(BILL_IAM_SEC, PRICE); yield2 = BILL_IAM_SEC.accept(YFPC, PRICE); assertEquals( "Bill Security: discounting method - yield from price", yield1, yield2, TOLERANCE_YIELD); } }
/** * Tests related to the pricing method for digital Forex option transactions with Black function and * a volatility provider. */ public class ForexOptionDigitalCallSpreadMethodTest { // General private static final Calendar CALENDAR = new MondayToFridayCalendar("A"); private static final BusinessDayConvention BUSINESS_DAY = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("Modified Following"); private static final int SETTLEMENT_DAYS = 2; // Smile data private static final Currency EUR = Currency.EUR; private static final Currency USD = Currency.USD; private static final ZonedDateTime REFERENCE_DATE = DateUtils.getUTCDate(2011, 6, 13); private static final FXMatrix FX_MATRIX = TestsDataSetsForex.fxMatrix(); private static final double SPOT = FX_MATRIX.getFxRate(EUR, USD); private static final SmileDeltaTermStructureParameter SMILE_TERM = TestsDataSetsForex.smile(REFERENCE_DATE); private static final SmileDeltaTermStructureParameter SMILE_TERM_FLAT = TestsDataSetsForex.smileFlat(REFERENCE_DATE); // Methods and curves private static final YieldCurveBundle CURVES = TestsDataSetsForex.createCurvesForex(); private static final String[] CURVES_NAME = TestsDataSetsForex.curveNames(); private static final Map<String, Currency> CURVE_CURRENCY = TestsDataSetsForex.curveCurrency(); private static final SmileDeltaTermStructureDataBundle SMILE_BUNDLE = new SmileDeltaTermStructureDataBundle( FX_MATRIX, CURVE_CURRENCY, CURVES, SMILE_TERM, Pair.of(EUR, USD)); private static final SmileDeltaTermStructureDataBundle SMILE_BUNDLE_FLAT = new SmileDeltaTermStructureDataBundle( FX_MATRIX, CURVE_CURRENCY, CURVES, SMILE_TERM_FLAT, Pair.of(EUR, USD)); private static final ForexOptionVanillaBlackMethod METHOD_VANILLA_BLACK = ForexOptionVanillaBlackMethod.getInstance(); private static final ForexOptionDigitalBlackMethod METHOD_DIGITAL_BLACK = ForexOptionDigitalBlackMethod.getInstance(); private static final double STANDARD_SPREAD = 0.0001; private static final ForexOptionDigitalCallSpreadBlackMethod METHOD_DIGITAL_SPREAD = new ForexOptionDigitalCallSpreadBlackMethod(STANDARD_SPREAD); private static final PresentValueCalculator PVC = PresentValueCalculator.getInstance(); // option private static final double STRIKE = 1.45; private static final boolean IS_CALL = true; private static final boolean IS_LONG = true; private static final double NOTIONAL = 100000000; private static final ZonedDateTime OPTION_PAY_DATE = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); private static final ZonedDateTime OPTION_EXP_DATE = ScheduleCalculator.getAdjustedDate(OPTION_PAY_DATE, -SETTLEMENT_DAYS, CALENDAR); private static final ForexDefinition FOREX_DEFINITION = new ForexDefinition(EUR, USD, OPTION_PAY_DATE, NOTIONAL, STRIKE); private static final Forex FOREX = FOREX_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final ForexOptionDigitalDefinition FOREX_OPTION_CALL_DEFINITION = new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, IS_CALL, IS_LONG); private static final ForexOptionDigital FOREX_CALL_OPTION = FOREX_OPTION_CALL_DEFINITION.toDerivative(REFERENCE_DATE, CURVES_NAME); private static final double TOLERANCE_PRICE = 1.0E-2; private static final double TOLERANCE_PRICE_FLAT = 1.0E+1; // The spread size will create a discrepancy. private static final double TOLERANCE_CE_FLAT = 1.0E+2; // The spread size will create a discrepancy. private static final double TOLERANCE_DELTA = 1.0E+2; // 0.01 currency unit for 1 bp @Test /** Tests the present value in a flat smile case. */ public void presentValueFlat() { MultipleCurrencyAmount pvSpread = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT); MultipleCurrencyAmount pvBlack = METHOD_DIGITAL_BLACK.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT); assertEquals( "Forex Digital option: call spread method - present value", pvBlack.getAmount(USD), pvSpread.getAmount(USD), TOLERANCE_PRICE_FLAT); } @Test /** Tests the present value with an explicit computation. */ public void presentValue() { double strikeM = STRIKE * (1 - STANDARD_SPREAD); double strikeP = STRIKE * (1 + STANDARD_SPREAD); Forex forexM = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeM)); Forex forexP = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeP)); ForexOptionVanilla vanillaM = new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); ForexOptionVanilla vanillaP = new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); MultipleCurrencyAmount pvP = METHOD_VANILLA_BLACK.presentValue(vanillaP, SMILE_BUNDLE); MultipleCurrencyAmount pvM = METHOD_VANILLA_BLACK.presentValue(vanillaM, SMILE_BUNDLE); MultipleCurrencyAmount pvExpected = pvM.plus(pvP.multipliedBy(-1.0)) .multipliedBy( 1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount())); MultipleCurrencyAmount pvComputed = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - present value", pvExpected.getAmount(USD), pvComputed.getAmount(USD), TOLERANCE_PRICE); } @Test /** Tests put call parity. */ public void presentValuePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition callDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition putDefinition = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital call = callDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital put = putDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount pvCall = METHOD_DIGITAL_SPREAD.presentValue(call, SMILE_BUNDLE); final MultipleCurrencyAmount pvPut = METHOD_DIGITAL_SPREAD.presentValue(put, SMILE_BUNDLE); final Double pvCash = PVC.visit(put.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: call spread method - present value", pvCall.getAmount(USD) + pvPut.getAmount(USD), Math.abs(pvCash), TOLERANCE_PRICE_FLAT); } @Test /** Tests the present value long/short parity. */ public void presentValueLongShort() { final ForexOptionDigitalDefinition forexOptionShortDefinition = new ForexOptionDigitalDefinition(FOREX_DEFINITION, OPTION_EXP_DATE, IS_CALL, !IS_LONG); final InstrumentDerivative forexOptionShort = forexOptionShortDefinition.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount pvShort = METHOD_DIGITAL_SPREAD.presentValue(forexOptionShort, SMILE_BUNDLE); final MultipleCurrencyAmount pvLong = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: present value long/short parity", pvLong.getAmount(USD), -pvShort.getAmount(USD), 1E-2); final MultipleCurrencyAmount ceShort = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionShort, SMILE_BUNDLE); final MultipleCurrencyAmount ceLong = METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: currency exposure long/short parity", ceLong.getAmount(USD), -ceShort.getAmount(USD), 1E-2); assertEquals( "Forex Digital option: currency exposure long/short parity", ceLong.getAmount(EUR), -ceShort.getAmount(EUR), 1E-2); } @Test /** Tests the currency exposure in a flat smile case. */ public void currencyExposureFlat() { MultipleCurrencyAmount ceSpread = METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT); MultipleCurrencyAmount ceBlack = METHOD_DIGITAL_BLACK.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE_FLAT); assertEquals( "Forex Digital option: call spread method - currency exposure", ceBlack.getAmount(USD), ceSpread.getAmount(USD), TOLERANCE_CE_FLAT); assertEquals( "Forex Digital option: call spread method - currency exposure", ceBlack.getAmount(EUR), ceSpread.getAmount(EUR), TOLERANCE_CE_FLAT); } @Test /** Tests the currency exposure with an explicit computation. */ public void currencyExposure() { double spread = 0.0001; // Relative spread. double strikeM = STRIKE * (1 - spread); double strikeP = STRIKE * (1 + spread); Forex forexM = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeM)); Forex forexP = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeP)); ForexOptionVanilla vanillaM = new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); ForexOptionVanilla vanillaP = new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); MultipleCurrencyAmount ceP = METHOD_VANILLA_BLACK.currencyExposure(vanillaP, SMILE_BUNDLE); MultipleCurrencyAmount ceM = METHOD_VANILLA_BLACK.currencyExposure(vanillaM, SMILE_BUNDLE); MultipleCurrencyAmount ceExpected = ceM.plus(ceP.multipliedBy(-1.0)) .multipliedBy( 1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount())); MultipleCurrencyAmount ceComputed = METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - currency exposure", ceExpected.getAmount(USD), ceComputed.getAmount(USD), TOLERANCE_PRICE); assertEquals( "Forex Digital option: call spread method - currency exposure", ceExpected.getAmount(EUR), ceComputed.getAmount(EUR), TOLERANCE_PRICE); } @Test /** Tests the currency exposure against the present value. */ public void currencyExposureVsPresentValue() { MultipleCurrencyAmount pv = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); MultipleCurrencyAmount ce = METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - currency exposure vs present value", ce.getAmount(USD) + ce.getAmount(EUR) * SPOT, pv.getAmount(USD), TOLERANCE_PRICE); } @Test /** Tests the put/call parity currency exposure. */ public void currencyExposurePutCallParity() { final double strike = 1.45; final boolean isCall = true; final boolean isLong = true; final double notional = 100000000; final ZonedDateTime payDate = ScheduleCalculator.getAdjustedDate( REFERENCE_DATE, Period.ofMonths(9), BUSINESS_DAY, CALENDAR); final ZonedDateTime expDate = ScheduleCalculator.getAdjustedDate(payDate, -SETTLEMENT_DAYS, CALENDAR); final ForexDefinition forexUnderlyingDefinition = new ForexDefinition(EUR, USD, payDate, notional, strike); final ForexOptionDigitalDefinition forexOptionDefinitionCall = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, isCall, isLong); final ForexOptionDigitalDefinition forexOptionDefinitionPut = new ForexOptionDigitalDefinition(forexUnderlyingDefinition, expDate, !isCall, isLong); final ForexOptionDigital forexOptionCall = forexOptionDefinitionCall.toDerivative(REFERENCE_DATE, CURVES_NAME); final ForexOptionDigital forexOptionPut = forexOptionDefinitionPut.toDerivative(REFERENCE_DATE, CURVES_NAME); final MultipleCurrencyAmount currencyExposureCall = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionCall, SMILE_BUNDLE); final MultipleCurrencyAmount currencyExposurePut = METHOD_DIGITAL_SPREAD.currencyExposure(forexOptionPut, SMILE_BUNDLE); final Double pvCash = PVC.visit(forexOptionPut.getUnderlyingForex().getPaymentCurrency2(), CURVES); assertEquals( "Forex Digital option: currency exposure put/call parity foreign", 0, currencyExposureCall.getAmount(EUR) + currencyExposurePut.getAmount(EUR), TOLERANCE_PRICE); assertEquals( "Forex Digital option: currency exposure put/call parity domestic", Math.abs(pvCash), currencyExposureCall.getAmount(USD) + currencyExposurePut.getAmount(USD), TOLERANCE_PRICE); } @Test /** Tests the present value curve sensitivity. */ public void presentValueCurveSensitivity() { double spread = 0.0001; // Relative spread. double strikeM = STRIKE * (1 - spread); double strikeP = STRIKE * (1 + spread); Forex forexM = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeM)); Forex forexP = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeP)); ForexOptionVanilla vanillaM = new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); ForexOptionVanilla vanillaP = new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); MultipleCurrencyInterestRateCurveSensitivity pvcsP = METHOD_VANILLA_BLACK.presentValueCurveSensitivity(vanillaP, SMILE_BUNDLE); MultipleCurrencyInterestRateCurveSensitivity pvcsM = METHOD_VANILLA_BLACK.presentValueCurveSensitivity(vanillaM, SMILE_BUNDLE); MultipleCurrencyInterestRateCurveSensitivity pvcsExpected = pvcsM .plus(pvcsP.multipliedBy(-1.0)) .multipliedBy( 1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount())); MultipleCurrencyInterestRateCurveSensitivity pvcsComputed = METHOD_DIGITAL_SPREAD.presentValueCurveSensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE); assertTrue( "Forex Digital option: call spread method - present value", MultipleCurrencyInterestRateCurveSensitivity.compare( pvcsExpected, pvcsComputed, TOLERANCE_DELTA)); } @Test /** Tests the present value curve sensitivity. */ public void presentValueVolatilitySensitivity() { double strikeM = STRIKE * (1 - STANDARD_SPREAD); double strikeP = STRIKE * (1 + STANDARD_SPREAD); Forex forexM = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeM)); Forex forexP = new Forex( FOREX.getPaymentCurrency1().withAmount(1.0), FOREX.getPaymentCurrency2().withAmount(-strikeP)); ForexOptionVanilla vanillaM = new ForexOptionVanilla(forexM, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); ForexOptionVanilla vanillaP = new ForexOptionVanilla(forexP, FOREX_CALL_OPTION.getExpirationTime(), IS_CALL, IS_LONG); PresentValueForexBlackVolatilitySensitivity pvbvP = METHOD_VANILLA_BLACK.presentValueVolatilitySensitivity(vanillaP, SMILE_BUNDLE); PresentValueForexBlackVolatilitySensitivity pvbvM = METHOD_VANILLA_BLACK.presentValueVolatilitySensitivity(vanillaM, SMILE_BUNDLE); PresentValueForexBlackVolatilitySensitivity pvbvExpected = pvbvM .plus(pvbvP.multipliedBy(-1.0)) .multipliedBy( 1.0 / (strikeP - strikeM) * Math.abs(FOREX.getPaymentCurrency2().getAmount())); PresentValueForexBlackVolatilitySensitivity pvbvComputed = METHOD_DIGITAL_SPREAD.presentValueVolatilitySensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - present value volatility sensitivity", pvbvComputed.getVega().getMap().size(), 2); assertTrue( "Forex Digital option: call spread method - present value volatility sensitivity", PresentValueForexBlackVolatilitySensitivity.compare( pvbvExpected, pvbvComputed, TOLERANCE_DELTA)); } @Test /** Tests the present value. Spread change. */ public void presentValueSpreadChange() { double spread1 = 0.0001; double spread2 = 0.0002; ForexOptionDigitalCallSpreadBlackMethod methodCallSpreadBlack1 = new ForexOptionDigitalCallSpreadBlackMethod(spread1); ForexOptionDigitalCallSpreadBlackMethod methodCallSpreadBlack2 = new ForexOptionDigitalCallSpreadBlackMethod(spread2); MultipleCurrencyAmount pv1 = methodCallSpreadBlack1.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); MultipleCurrencyAmount pv2 = methodCallSpreadBlack2.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - present value", pv1.getAmount(USD), pv2.getAmount(USD), 10.0); // MultipleCurrencyAmount pvBlack = METHOD_DIGITAL_BLACK.presentValue(FOREX_CALL_OPTION, // SMILE_BUNDLE); // assertEquals("Forex Digital option: call spread method - present value", // pvBlack.getAmount(USD), pv2.getAmount(USD), 10.0); // Should fail } @Test /** Tests the present value. Method vs Calculator. */ public void presentValueMethodVCalculator() { MultipleCurrencyAmount pv1 = METHOD_DIGITAL_SPREAD.presentValue(FOREX_CALL_OPTION, SMILE_BUNDLE); PresentValueCallSpreadBlackForexCalculator calculator = new PresentValueCallSpreadBlackForexCalculator(STANDARD_SPREAD); MultipleCurrencyAmount pvCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - present value", pv1.getAmount(USD), pvCalculator.getAmount(USD), TOLERANCE_PRICE); } @Test /** Tests the currency exposure. Method vs Calculator. */ public void currencyExposureMethodVCalculator() { MultipleCurrencyAmount ceMethod = METHOD_DIGITAL_SPREAD.currencyExposure(FOREX_CALL_OPTION, SMILE_BUNDLE); CurrencyExposureCallSpreadBlackForexCalculator calculator = new CurrencyExposureCallSpreadBlackForexCalculator(STANDARD_SPREAD); MultipleCurrencyAmount ceCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE); assertEquals( "Forex Digital option: call spread method - currency exposure", ceMethod.getAmount(USD), ceCalculator.getAmount(USD), TOLERANCE_PRICE); assertEquals( "Forex Digital option: call spread method - currency exposure", ceMethod.getAmount(EUR), ceCalculator.getAmount(EUR), TOLERANCE_PRICE); } @Test /** Tests the present value curve sensitivity. Method vs Calculator. */ public void presentValueCurveSensitivityMethodVCalculator() { MultipleCurrencyInterestRateCurveSensitivity pvcsMethod = METHOD_DIGITAL_SPREAD.presentValueCurveSensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE); PresentValueCurveSensitivityCallSpreadBlackForexCalculator calculator = new PresentValueCurveSensitivityCallSpreadBlackForexCalculator(STANDARD_SPREAD); MultipleCurrencyInterestRateCurveSensitivity pvcsCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE); assertTrue( "Forex Digital option: call spread method - present value", MultipleCurrencyInterestRateCurveSensitivity.compare( pvcsMethod, pvcsCalculator, TOLERANCE_DELTA)); } @Test /** Tests the present value volatility sensitivity. Method vs Calculator. */ public void presentValueVolatilitySensitivityMethodVCalculator() { PresentValueForexBlackVolatilitySensitivity pvbvMethod = METHOD_DIGITAL_SPREAD.presentValueVolatilitySensitivity(FOREX_CALL_OPTION, SMILE_BUNDLE); PresentValueVolatilitySensitivityCallSpreadBlackForexCalculator calculator = new PresentValueVolatilitySensitivityCallSpreadBlackForexCalculator(STANDARD_SPREAD); PresentValueForexBlackVolatilitySensitivity pvbvCalculator = calculator.visit(FOREX_CALL_OPTION, SMILE_BUNDLE); assertTrue( "Forex Digital option: call spread method - present value volatility sensitivity", PresentValueForexBlackVolatilitySensitivity.compare( pvbvMethod, pvbvCalculator, TOLERANCE_DELTA)); } }