static { CurveInterpolator interp = CurveInterpolators.DOUBLE_QUADRATIC; DoubleArray time_gbp = DoubleArray.of(0.0, 0.1, 0.25, 0.5, 0.75, 1.0, 2.0); DoubleArray rate_gbp = DoubleArray.of(0.0160, 0.0165, 0.0155, 0.0155, 0.0155, 0.0150, 0.014); InterpolatedNodalCurve dscCurve = InterpolatedNodalCurve.of( Curves.zeroRates("GBP-Discount", DAY_COUNT), time_gbp, rate_gbp, interp); DoubleArray time_index = DoubleArray.of(0.0, 0.25, 0.5, 1.0); DoubleArray rate_index = DoubleArray.of(0.0180, 0.0180, 0.0175, 0.0165); InterpolatedNodalCurve indexCurve = InterpolatedNodalCurve.of( Curves.zeroRates("GBP-GBPIBOR3M", DAY_COUNT), time_index, rate_index, interp); IMM_PROV = ImmutableRatesProvider.builder() .valuationDate(VAL_DATE) .discountCurves(ImmutableMap.of(GBP, dscCurve)) .indexCurves(ImmutableMap.of(GBP_LIBOR_3M, indexCurve)) .build(); }
/** Test parameter sensitivity with finite difference sensitivity calculator. No cutoff period. */ public void rateChfNoCutOffParameterSensitivity() { LocalDate[] valuationDate = {date(2015, 1, 1), date(2015, 1, 8)}; double[] time = new double[] {0.0, 0.5, 1.0, 2.0, 5.0, 10.0}; double[] rate = new double[] {0.0100, 0.0110, 0.0115, 0.0130, 0.0135, 0.0135}; for (int loopvaldate = 0; loopvaldate < 2; loopvaldate++) { Curve onCurve = InterpolatedNodalCurve.of(Curves.zeroRates("ON", ACT_ACT_ISDA), time, rate, INTERPOLATOR); ImmutableRatesProvider prov = ImmutableRatesProvider.builder() .valuationDate(valuationDate[loopvaldate]) .indexCurves(ImmutableMap.of(CHF_TOIS, onCurve)) .timeSeries(ImmutableMap.of(CHF_TOIS, TIME_SERIES_BUILDER.build())) .build(); OvernightAveragedRateObservation ro = OvernightAveragedRateObservation.of(CHF_TOIS, FIXING_START_DATE, FIXING_END_DATE, 0); ForwardOvernightAveragedRateObservationFn obsFn = ForwardOvernightAveragedRateObservationFn.DEFAULT; PointSensitivityBuilder sensitivityBuilderComputed = obsFn.rateSensitivity(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, prov); CurveCurrencyParameterSensitivities parameterSensitivityComputed = prov.curveParameterSensitivity(sensitivityBuilderComputed.build()); CurveCurrencyParameterSensitivities parameterSensitivityExpected = CAL_FD.sensitivity( prov, (p) -> CurrencyAmount.of( CHF_TOIS.getCurrency(), obsFn.rate(ro, DUMMY_ACCRUAL_START_DATE, DUMMY_ACCRUAL_END_DATE, (p)))); assertTrue( parameterSensitivityComputed.equalWithTolerance( parameterSensitivityExpected, EPS_FD * 10.0)); } }
/** Test {@link IssuerCurveDiscountFactors}. */ @Test public class IssuerCurveDiscountFactorsTest { private static final LocalDate DATE = date(2015, 6, 4); private static final LocalDate DATE_AFTER = date(2015, 7, 30); private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final CurveName NAME = CurveName.of("TestCurve"); private static final CurveMetadata METADATA = Curves.zeroRates(NAME, ACT_365F); private static final InterpolatedNodalCurve CURVE = InterpolatedNodalCurve.of( METADATA, DoubleArray.of(0, 10), DoubleArray.of(1, 2), INTERPOLATOR); private static final DiscountFactors DSC_FACTORS = ZeroRateDiscountFactors.of(GBP, DATE, CURVE); private static final LegalEntityGroup GROUP = LegalEntityGroup.of("ISSUER1"); public void test_of() { IssuerCurveDiscountFactors test = IssuerCurveDiscountFactors.of(DSC_FACTORS, GROUP); assertEquals(test.getLegalEntityGroup(), GROUP); assertEquals(test.getCurrency(), GBP); assertEquals(test.getCurveName(), NAME); assertEquals(test.getParameterCount(), 2); assertEquals(test.getValuationDate(), DATE); assertEquals(test.discountFactor(DATE_AFTER), DSC_FACTORS.discountFactor(DATE_AFTER)); } public void test_zeroRatePointSensitivity() { IssuerCurveDiscountFactors base = IssuerCurveDiscountFactors.of(DSC_FACTORS, GROUP); IssuerCurveZeroRateSensitivity expected = IssuerCurveZeroRateSensitivity.of(DSC_FACTORS.zeroRatePointSensitivity(DATE_AFTER), GROUP); IssuerCurveZeroRateSensitivity computed = base.zeroRatePointSensitivity(DATE_AFTER); assertEquals(computed, expected); } public void test_zeroRatePointSensitivity_USD() { IssuerCurveDiscountFactors base = IssuerCurveDiscountFactors.of(DSC_FACTORS, GROUP); IssuerCurveZeroRateSensitivity expected = IssuerCurveZeroRateSensitivity.of( DSC_FACTORS.zeroRatePointSensitivity(DATE_AFTER, USD), GROUP); IssuerCurveZeroRateSensitivity computed = base.zeroRatePointSensitivity(DATE_AFTER, USD); assertEquals(computed, expected); } public void test_curveParameterSensitivity() { IssuerCurveDiscountFactors base = IssuerCurveDiscountFactors.of(DSC_FACTORS, GROUP); IssuerCurveZeroRateSensitivity sensi = base.zeroRatePointSensitivity(DATE_AFTER, USD); CurveCurrencyParameterSensitivities computed = base.curveParameterSensitivity(sensi); CurveCurrencyParameterSensitivities expected = DSC_FACTORS.curveParameterSensitivity( DSC_FACTORS.zeroRatePointSensitivity(DATE_AFTER, USD)); assertEquals(computed, expected); } // ------------------------------------------------------------------------- public void coverage() { IssuerCurveDiscountFactors test1 = IssuerCurveDiscountFactors.of(DSC_FACTORS, GROUP); coverImmutableBean(test1); IssuerCurveDiscountFactors test2 = IssuerCurveDiscountFactors.of( ZeroRateDiscountFactors.of(USD, DATE, CURVE), LegalEntityGroup.of("ISSUER2")); coverBeanEquals(test1, test2); } }
/** Test {@link DiscountFxForwardRates}. */ @Test public class DiscountFxForwardRatesTest { private static final LocalDate DATE_VAL = date(2015, 6, 4); private static final LocalDate DATE_REF = date(2015, 7, 30); private static final FxRate FX_RATE = FxRate.of(GBP, USD, 1.5d); private static final CurrencyPair CURRENCY_PAIR = CurrencyPair.of(GBP, USD); private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final CurveMetadata METADATA1 = Curves.zeroRates("TestCurve", ACT_365F); private static final CurveMetadata METADATA2 = Curves.zeroRates("TestCurveUSD", ACT_365F); private static final InterpolatedNodalCurve CURVE1 = InterpolatedNodalCurve.of( METADATA1, DoubleArray.of(0, 10), DoubleArray.of(0.01, 0.02), INTERPOLATOR); private static final InterpolatedNodalCurve CURVE2 = InterpolatedNodalCurve.of( METADATA2, DoubleArray.of(0, 10), DoubleArray.of(0.015, 0.025), INTERPOLATOR); private static final ZeroRateDiscountFactors DFCURVE_GBP = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE1); private static final ZeroRateDiscountFactors DFCURVE_GBP2 = ZeroRateDiscountFactors.of(GBP, DATE_VAL, CURVE2); private static final ZeroRateDiscountFactors DFCURVE_USD = ZeroRateDiscountFactors.of(USD, DATE_VAL, CURVE2); private static final ZeroRateDiscountFactors DFCURVE_USD2 = ZeroRateDiscountFactors.of(USD, DATE_VAL, CURVE1); // ------------------------------------------------------------------------- public void test_of() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertEquals(test.getCurrencyPair(), CURRENCY_PAIR); assertEquals(test.getValuationDate(), DATE_VAL); assertEquals(test.getBaseCurrencyDiscountFactors(), DFCURVE_GBP); assertEquals(test.getCounterCurrencyDiscountFactors(), DFCURVE_USD); assertEquals(test.getFxRateProvider(), FX_RATE); assertEquals(test.findData(CURVE1.getName()), Optional.of(CURVE1)); assertEquals(test.findData(CURVE2.getName()), Optional.of(CURVE2)); assertEquals(test.findData(CurveName.of("Rubbish")), Optional.empty()); int baseSize = DFCURVE_USD.getParameterCount(); assertEquals(test.getParameterCount(), DFCURVE_GBP.getParameterCount() + baseSize); assertEquals(test.getParameter(0), DFCURVE_GBP.getParameter(0)); assertEquals(test.getParameter(baseSize), DFCURVE_USD.getParameter(0)); assertEquals(test.getParameterMetadata(0), DFCURVE_GBP.getParameterMetadata(0)); assertEquals(test.getParameterMetadata(baseSize), DFCURVE_USD.getParameterMetadata(0)); assertEquals( test.withParameter(0, 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP.withParameter(0, 1d)); assertEquals(test.withParameter(0, 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD); assertEquals(test.withParameter(baseSize, 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP); assertEquals( test.withParameter(baseSize, 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD.withParameter(0, 1d)); assertEquals( test.withPerturbation((i, v, m) -> v + 1d).getBaseCurrencyDiscountFactors(), DFCURVE_GBP.withPerturbation((i, v, m) -> v + 1d)); assertEquals( test.withPerturbation((i, v, m) -> v + 1d).getCounterCurrencyDiscountFactors(), DFCURVE_USD.withPerturbation((i, v, m) -> v + 1d)); } public void test_of_nonMatchingCurrency() { assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_GBP)); assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_USD, DFCURVE_USD)); } public void test_of_nonMatchingValuationDates() { DiscountFactors curve2 = ZeroRateDiscountFactors.of(USD, DATE_REF, CURVE2); assertThrowsIllegalArg( () -> DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, curve2)); } public void test_builder() { assertThrowsIllegalArg( () -> DiscountFxForwardRates.meta() .builder() .setString(DiscountFxForwardRates.meta().currencyPair(), "GBP/USD") .build()); assertThrowsIllegalArg( () -> DiscountFxForwardRates.meta() .builder() .setString(DiscountFxForwardRates.meta().currencyPair().name(), "GBP/USD") .build()); } // ------------------------------------------------------------------------- public void test_withDiscountFactors() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); test = test.withDiscountFactors(DFCURVE_GBP2, DFCURVE_USD2); assertEquals(test.getCurrencyPair(), CURRENCY_PAIR); assertEquals(test.getValuationDate(), DATE_VAL); assertEquals(test.getBaseCurrencyDiscountFactors(), DFCURVE_GBP2); assertEquals(test.getCounterCurrencyDiscountFactors(), DFCURVE_USD2); assertEquals(test.getFxRateProvider(), FX_RATE); } // ------------------------------------------------------------------------- public void test_rate() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(DATE_REF); double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(DATE_REF); double expected = FX_RATE.fxRate(GBP, USD) * (dfCcyBaseAtMaturity / dfCcyCounterAtMaturity); assertEquals(test.rate(GBP, DATE_REF), expected, 1e-12); assertEquals(test.rate(USD, DATE_REF), 1d / expected, 1e-12); } public void test_rate_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.rate(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- public void test_ratePointSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertEquals( test.ratePointSensitivity(GBP, DATE_REF), FxForwardSensitivity.of(CURRENCY_PAIR, GBP, DATE_REF, 1d)); assertEquals( test.ratePointSensitivity(USD, DATE_REF), FxForwardSensitivity.of(CURRENCY_PAIR, USD, DATE_REF, 1d)); } public void test_ratePointSensitivity_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.ratePointSensitivity(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- public void test_rateFxSpotSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); double dfCcyBaseAtMaturity = DFCURVE_GBP.discountFactor(DATE_REF); double dfCcyCounterAtMaturity = DFCURVE_USD.discountFactor(DATE_REF); double expected = dfCcyBaseAtMaturity / dfCcyCounterAtMaturity; assertEquals(test.rateFxSpotSensitivity(GBP, DATE_REF), expected, 1e-12); assertEquals(test.rateFxSpotSensitivity(USD, DATE_REF), 1d / expected, 1e-12); } public void test_rateFxSpotSensitivity_nonMatchingCurrency() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); assertThrowsIllegalArg(() -> test.rateFxSpotSensitivity(EUR, DATE_VAL)); } // ------------------------------------------------------------------------- // proper end-to-end tests are elsewhere public void test_parameterSensitivity() { DiscountFxForwardRates test = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); FxForwardSensitivity point = FxForwardSensitivity.of(CURRENCY_PAIR, GBP, DATE_VAL, 1d); assertEquals(test.parameterSensitivity(point).size(), 2); FxForwardSensitivity point2 = FxForwardSensitivity.of(CURRENCY_PAIR, USD, DATE_VAL, 1d); assertEquals(test.parameterSensitivity(point2).size(), 2); } // ------------------------------------------------------------------------- public void coverage() { DiscountFxForwardRates test1 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE, DFCURVE_GBP, DFCURVE_USD); coverImmutableBean(test1); DiscountFxForwardRates test2 = DiscountFxForwardRates.of(CURRENCY_PAIR, FX_RATE.inverse(), DFCURVE_GBP2, DFCURVE_USD2); coverBeanEquals(test1, test2); DiscountFxForwardRates test3 = DiscountFxForwardRates.of( CurrencyPair.of(USD, EUR), FxRate.of(EUR, USD, 1.2d), DFCURVE_USD, ZeroRateDiscountFactors.of(EUR, DATE_VAL, CURVE2)); coverBeanEquals(test1, test3); } }
/** Test {@link AddFixedCurve}. */ @Test public class AddFixedCurveTest { private static final String NAME_FIXED = "FixedCurve"; private static final String NAME_SPREAD = "SpreadCurve"; private static final CurveName FIXED_CURVE_NAME = CurveName.of(NAME_FIXED); private static final CurveName SPREAD_CURVE_NAME = CurveName.of(NAME_SPREAD); private static final CurveMetadata METADATA_FIXED = Curves.zeroRates(FIXED_CURVE_NAME, ACT_365F); private static final String LABEL_1 = "Node1"; private static final String LABEL_2 = "Node2"; private static final String LABEL_3 = "Node3"; private static final List<ParameterMetadata> PARAM_METADATA_SPREAD = new ArrayList<>(); static { PARAM_METADATA_SPREAD.add(LabelDateParameterMetadata.of(LocalDate.of(2015, 1, 1), LABEL_1)); PARAM_METADATA_SPREAD.add(LabelDateParameterMetadata.of(LocalDate.of(2015, 2, 1), LABEL_2)); PARAM_METADATA_SPREAD.add(LabelDateParameterMetadata.of(LocalDate.of(2015, 3, 1), LABEL_3)); } private static final CurveMetadata METADATA_SPREAD = DefaultCurveMetadata.builder() .curveName(SPREAD_CURVE_NAME) .xValueType(ValueType.YEAR_FRACTION) .yValueType(ValueType.ZERO_RATE) .dayCount(ACT_365F) .parameterMetadata(PARAM_METADATA_SPREAD) .build(); private static final DoubleArray XVALUES_FIXED = DoubleArray.of(1d, 2d, 3d, 4d); private static final DoubleArray YVALUES_FIXED = DoubleArray.of(0.05d, 0.07d, 0.08d, 0.09d); private static final DoubleArray XVALUES_SPREAD = DoubleArray.of(1.5d, 2.5d, 4.5d); private static final DoubleArray YVALUES_SPREAD = DoubleArray.of(0.04d, 0.045d, 0.05d); private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final double[] X_SAMPLE = {0.5d, 1.0d, 1.5d, 1.75d, 10.0d}; private static final int NB_X_SAMPLE = X_SAMPLE.length; private static final InterpolatedNodalCurve FIXED_CURVE = InterpolatedNodalCurve.of(METADATA_FIXED, XVALUES_FIXED, YVALUES_FIXED, INTERPOLATOR); private static final InterpolatedNodalCurve SPREAD_CURVE = InterpolatedNodalCurve.of(METADATA_SPREAD, XVALUES_SPREAD, YVALUES_SPREAD, INTERPOLATOR); private static final AddFixedCurve ADD_FIXED_CURVE = AddFixedCurve.of(FIXED_CURVE, SPREAD_CURVE); private static final double TOLERANCE_Y = 1.0E-10; public void test_invalid() { // null fixed assertThrowsIllegalArg(() -> AddFixedCurve.of(null, SPREAD_CURVE)); // null spread assertThrowsIllegalArg(() -> AddFixedCurve.of(FIXED_CURVE, null)); } public void getter() { assertEquals(ADD_FIXED_CURVE.getMetadata(), METADATA_SPREAD); assertEquals(ADD_FIXED_CURVE.getParameterCount(), XVALUES_SPREAD.size()); assertEquals(ADD_FIXED_CURVE.getParameter(0), ADD_FIXED_CURVE.getSpreadCurve().getParameter(0)); assertEquals( ADD_FIXED_CURVE.getParameterMetadata(0), ADD_FIXED_CURVE.getSpreadCurve().getParameterMetadata(0)); assertEquals( ADD_FIXED_CURVE.withParameter(0, 9d), AddFixedCurve.of(FIXED_CURVE, SPREAD_CURVE.withParameter(0, 9d))); assertEquals( ADD_FIXED_CURVE.withPerturbation((i, v, m) -> v + 1d), AddFixedCurve.of(FIXED_CURVE, SPREAD_CURVE.withPerturbation((i, v, m) -> v + 1d))); assertEquals( ADD_FIXED_CURVE.withMetadata(METADATA_FIXED), AddFixedCurve.of(FIXED_CURVE, SPREAD_CURVE.withMetadata(METADATA_FIXED))); } public void yValue() { for (int i = 0; i < NB_X_SAMPLE; i++) { double yComputed = ADD_FIXED_CURVE.yValue(X_SAMPLE[i]); double yExpected = FIXED_CURVE.yValue(X_SAMPLE[i]) + SPREAD_CURVE.yValue(X_SAMPLE[i]); assertEquals(yComputed, yExpected, TOLERANCE_Y); } } public void firstDerivative() { for (int i = 0; i < NB_X_SAMPLE; i++) { double dComputed = ADD_FIXED_CURVE.firstDerivative(X_SAMPLE[i]); double dExpected = FIXED_CURVE.firstDerivative(X_SAMPLE[i]) + SPREAD_CURVE.firstDerivative(X_SAMPLE[i]); assertEquals(dComputed, dExpected, TOLERANCE_Y); } } public void yParameterSensitivity() { for (int i = 0; i < X_SAMPLE.length; i++) { UnitParameterSensitivity dComputed = ADD_FIXED_CURVE.yValueParameterSensitivity(X_SAMPLE[i]); UnitParameterSensitivity dExpected = SPREAD_CURVE.yValueParameterSensitivity(X_SAMPLE[i]); assertTrue(dComputed.compareKey(dExpected) == 0); assertTrue( dComputed.getSensitivity().equalWithTolerance(dExpected.getSensitivity(), TOLERANCE_Y)); } } // ------------------------------------------------------------------------- public void coverage() { coverImmutableBean(ADD_FIXED_CURVE); coverBeanEquals(ADD_FIXED_CURVE, AddFixedCurve.of(SPREAD_CURVE, FIXED_CURVE)); } }
/** Test */ @Test public class DiscountingFixedCouponBondProductPricerTest { // fixed coupon bond private static final StandardId SECURITY_ID = StandardId.of("OG-Ticker", "GOVT1-BOND1"); private static final StandardId ISSUER_ID = StandardId.of("OG-Ticker", "GOVT1"); private static final LocalDate VAL_DATE = date(2016, 4, 25); private static final YieldConvention YIELD_CONVENTION = YieldConvention.GERMAN_BONDS; private static final double NOTIONAL = 1.0e7; private static final double FIXED_RATE = 0.015; private static final HolidayCalendar EUR_CALENDAR = HolidayCalendars.EUTA; private static final DaysAdjustment DATE_OFFSET = DaysAdjustment.ofBusinessDays(3, EUR_CALENDAR); private static final DayCount DAY_COUNT = DayCounts.ACT_365F; private static final LocalDate START_DATE = LocalDate.of(2015, 4, 12); private static final LocalDate END_DATE = LocalDate.of(2025, 4, 12); private static final BusinessDayAdjustment BUSINESS_ADJUST = BusinessDayAdjustment.of(BusinessDayConventions.MODIFIED_FOLLOWING, EUR_CALENDAR); private static final PeriodicSchedule PERIOD_SCHEDULE = PeriodicSchedule.of( START_DATE, END_DATE, Frequency.P6M, BUSINESS_ADJUST, StubConvention.SHORT_INITIAL, false); private static final DaysAdjustment EX_COUPON = DaysAdjustment.ofBusinessDays(-5, EUR_CALENDAR, BUSINESS_ADJUST); /** nonzero ex-coupon period */ private static final FixedCouponBond PRODUCT = FixedCouponBond.builder() .dayCount(DAY_COUNT) .fixedRate(FIXED_RATE) .legalEntityId(ISSUER_ID) .currency(EUR) .notional(NOTIONAL) .periodicSchedule(PERIOD_SCHEDULE) .settlementDateOffset(DATE_OFFSET) .yieldConvention(YIELD_CONVENTION) .exCouponPeriod(EX_COUPON) .build(); private static final Security<FixedCouponBond> BOND_SECURITY = UnitSecurity.builder(PRODUCT).standardId(SECURITY_ID).build(); /** no ex-coupon period */ private static final FixedCouponBond PRODUCT_NO_EXCOUPON = FixedCouponBond.builder() .dayCount(DAY_COUNT) .fixedRate(FIXED_RATE) .legalEntityId(ISSUER_ID) .currency(EUR) .notional(NOTIONAL) .periodicSchedule(PERIOD_SCHEDULE) .settlementDateOffset(DATE_OFFSET) .yieldConvention(YIELD_CONVENTION) .build(); // rates provider private static final CurveInterpolator INTERPOLATOR = CurveInterpolators.LINEAR; private static final CurveName NAME_REPO = CurveName.of("TestRepoCurve"); private static final CurveMetadata METADATA_REPO = Curves.zeroRates(NAME_REPO, ACT_365F); private static final InterpolatedNodalCurve CURVE_REPO = InterpolatedNodalCurve.of( METADATA_REPO, DoubleArray.of(0.1, 2.0, 10.0), DoubleArray.of(0.05, 0.06, 0.09), INTERPOLATOR); private static final DiscountFactors DSC_FACTORS_REPO = ZeroRateDiscountFactors.of(EUR, VAL_DATE, CURVE_REPO); private static final BondGroup GROUP_REPO = BondGroup.of("GOVT1 BOND1"); private static final CurveName NAME_ISSUER = CurveName.of("TestIssuerCurve"); private static final CurveMetadata METADATA_ISSUER = Curves.zeroRates(NAME_ISSUER, ACT_365F); private static final InterpolatedNodalCurve CURVE_ISSUER = InterpolatedNodalCurve.of( METADATA_ISSUER, DoubleArray.of(0.2, 9.0, 15.0), DoubleArray.of(0.03, 0.05, 0.13), INTERPOLATOR); private static final DiscountFactors DSC_FACTORS_ISSUER = ZeroRateDiscountFactors.of(EUR, VAL_DATE, CURVE_ISSUER); private static final LegalEntityGroup GROUP_ISSUER = LegalEntityGroup.of("GOVT1"); private static final LegalEntityDiscountingProvider PROVIDER = LegalEntityDiscountingProvider.builder() .issuerCurves( ImmutableMap.<Pair<LegalEntityGroup, Currency>, DiscountFactors>of( Pair.<LegalEntityGroup, Currency>of(GROUP_ISSUER, EUR), DSC_FACTORS_ISSUER)) .legalEntityMap(ImmutableMap.<StandardId, LegalEntityGroup>of(ISSUER_ID, GROUP_ISSUER)) .repoCurves( ImmutableMap.<Pair<BondGroup, Currency>, DiscountFactors>of( Pair.<BondGroup, Currency>of(GROUP_REPO, EUR), DSC_FACTORS_REPO)) .bondMap(ImmutableMap.<StandardId, BondGroup>of(SECURITY_ID, GROUP_REPO)) .valuationDate(VAL_DATE) .build(); private static final double Z_SPREAD = 0.035; private static final int PERIOD_PER_YEAR = 4; private static final double TOL = 1.0e-12; private static final double EPS = 1.0e-6; // pricers private static final DiscountingFixedCouponBondProductPricer PRICER = DiscountingFixedCouponBondProductPricer.DEFAULT; private static final DiscountingPaymentPricer PRICER_NOMINAL = DiscountingPaymentPricer.DEFAULT; private static final DiscountingFixedCouponBondPaymentPeriodPricer PRICER_COUPON = DiscountingFixedCouponBondPaymentPeriodPricer.DEFAULT; private static final RatesFiniteDifferenceSensitivityCalculator FD_CAL = new RatesFiniteDifferenceSensitivityCalculator(EPS); // ------------------------------------------------------------------------- public void test_presentValue() { CurrencyAmount computed = PRICER.presentValue(PRODUCT, PROVIDER); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue(expanded.getNominalPayment(), DSC_FACTORS_ISSUER); int size = expanded.getPeriodicPayments().size(); double pvCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvCupon += PRICER_COUPON.presentValue( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER)); } expected = expected.plus(pvCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } public void test_presentValueWithZSpread_continuous() { CurrencyAmount computed = PRICER.presentValueWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, CONTINUOUS, 0); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue( expanded.getNominalPayment(), DSC_FACTORS_ISSUER, Z_SPREAD, CONTINUOUS, 0); int size = expanded.getPeriodicPayments().size(); double pvcCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvcCupon += PRICER_COUPON.presentValueWithSpread( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER), Z_SPREAD, CONTINUOUS, 0); } expected = expected.plus(pvcCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } public void test_presentValueWithZSpread_periodic() { CurrencyAmount computed = PRICER.presentValueWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue( expanded.getNominalPayment(), DSC_FACTORS_ISSUER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); int size = expanded.getPeriodicPayments().size(); double pvcCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvcCupon += PRICER_COUPON.presentValueWithSpread( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER), Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); } expected = expected.plus(pvcCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } public void test_presentValue_noExcoupon() { CurrencyAmount computed = PRICER.presentValue(PRODUCT_NO_EXCOUPON, PROVIDER); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue(expanded.getNominalPayment(), DSC_FACTORS_ISSUER); int size = expanded.getPeriodicPayments().size(); double pvcCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvcCupon += PRICER_COUPON.presentValue( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER)); } expected = expected.plus(pvcCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } public void test_presentValueWithZSpread_continuous_noExcoupon() { CurrencyAmount computed = PRICER.presentValueWithZSpread(PRODUCT_NO_EXCOUPON, PROVIDER, Z_SPREAD, CONTINUOUS, 0); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue( expanded.getNominalPayment(), DSC_FACTORS_ISSUER, Z_SPREAD, CONTINUOUS, 0); int size = expanded.getPeriodicPayments().size(); double pvcCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvcCupon += PRICER_COUPON.presentValueWithSpread( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER), Z_SPREAD, CONTINUOUS, 0); } expected = expected.plus(pvcCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } public void test_presentValueWithZSpread_periodic_noExcoupon() { CurrencyAmount computed = PRICER.presentValueWithZSpread( PRODUCT_NO_EXCOUPON, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); ExpandedFixedCouponBond expanded = PRODUCT.expand(); CurrencyAmount expected = PRICER_NOMINAL.presentValue( expanded.getNominalPayment(), DSC_FACTORS_ISSUER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); int size = expanded.getPeriodicPayments().size(); double pvcCupon = 0d; for (int i = 2; i < size; ++i) { FixedCouponBondPaymentPeriod payment = expanded.getPeriodicPayments().get(i); pvcCupon += PRICER_COUPON.presentValueWithSpread( payment, IssuerCurveDiscountFactors.of(DSC_FACTORS_ISSUER, GROUP_ISSUER), Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); } expected = expected.plus(pvcCupon); assertEquals(computed.getCurrency(), EUR); assertEquals(computed.getAmount(), expected.getAmount(), NOTIONAL * TOL); } // ------------------------------------------------------------------------- public void test_dirtyPriceFromCurves() { double computed = PRICER.dirtyPriceFromCurves(BOND_SECURITY, PROVIDER); CurrencyAmount pv = PRICER.presentValue(PRODUCT, PROVIDER); LocalDate settlement = DATE_OFFSET.adjust(VAL_DATE); double df = DSC_FACTORS_REPO.discountFactor(settlement); assertEquals(computed, pv.getAmount() / df / NOTIONAL); } public void test_dirtyPriceFromCurvesWithZSpread_continuous() { double computed = PRICER.dirtyPriceFromCurvesWithZSpread(BOND_SECURITY, PROVIDER, Z_SPREAD, CONTINUOUS, 0); CurrencyAmount pv = PRICER.presentValueWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, CONTINUOUS, 0); LocalDate settlement = DATE_OFFSET.adjust(VAL_DATE); double df = DSC_FACTORS_REPO.discountFactor(settlement); assertEquals(computed, pv.getAmount() / df / NOTIONAL); } public void test_dirtyPriceFromCurvesWithZSpread_periodic() { double computed = PRICER.dirtyPriceFromCurvesWithZSpread( BOND_SECURITY, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); CurrencyAmount pv = PRICER.presentValueWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); LocalDate settlement = DATE_OFFSET.adjust(VAL_DATE); double df = DSC_FACTORS_REPO.discountFactor(settlement); assertEquals(computed, pv.getAmount() / df / NOTIONAL); } public void test_dirtyPriceFromCleanPrice_cleanPriceFromDirtyPrice() { double dirtyPrice = PRICER.dirtyPriceFromCurves(BOND_SECURITY, PROVIDER); LocalDate settlement = DATE_OFFSET.adjust(VAL_DATE); double cleanPrice = PRICER.cleanPriceFromDirtyPrice(PRODUCT, settlement, dirtyPrice); double accruedInterest = PRICER.accruedInterest(PRODUCT, settlement); assertEquals(cleanPrice, dirtyPrice - accruedInterest / NOTIONAL, NOTIONAL * TOL); double dirtyPriceRe = PRICER.dirtyPriceFromCleanPrice(PRODUCT, settlement, cleanPrice); assertEquals(dirtyPriceRe, dirtyPrice, TOL); } // ------------------------------------------------------------------------- public void test_zSpreadFromCurvesAndPV_continuous() { double dirtyPrice = PRICER.dirtyPriceFromCurvesWithZSpread(BOND_SECURITY, PROVIDER, Z_SPREAD, CONTINUOUS, 0); double computed = PRICER.zSpreadFromCurvesAndDirtyPrice(BOND_SECURITY, PROVIDER, dirtyPrice, CONTINUOUS, 0); assertEquals(computed, Z_SPREAD, TOL); } public void test_zSpreadFromCurvesAndPV_periodic() { double dirtyPrice = PRICER.dirtyPriceFromCurvesWithZSpread( BOND_SECURITY, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); double computed = PRICER.zSpreadFromCurvesAndDirtyPrice( BOND_SECURITY, PROVIDER, dirtyPrice, PERIODIC, PERIOD_PER_YEAR); assertEquals(computed, Z_SPREAD, TOL); } // ------------------------------------------------------------------------- public void test_presentValueSensitivity() { PointSensitivityBuilder point = PRICER.presentValueSensitivity(PRODUCT, PROVIDER); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity(PROVIDER, (p) -> PRICER.presentValue(PRODUCT, (p))); assertTrue(computed.equalWithTolerance(expected, 30d * NOTIONAL * EPS)); } public void test_presentValueSensitivityWithZSpread_continuous() { PointSensitivityBuilder point = PRICER.presentValueSensitivityWithZSpread(PRODUCT, PROVIDER, Z_SPREAD, CONTINUOUS, 0); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> PRICER.presentValueWithZSpread(PRODUCT, (p), Z_SPREAD, CONTINUOUS, 0)); assertTrue(computed.equalWithTolerance(expected, 20d * NOTIONAL * EPS)); } public void test_presentValueSensitivityWithZSpread_periodic() { PointSensitivityBuilder point = PRICER.presentValueSensitivityWithZSpread( PRODUCT, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> PRICER.presentValueWithZSpread(PRODUCT, (p), Z_SPREAD, PERIODIC, PERIOD_PER_YEAR)); assertTrue(computed.equalWithTolerance(expected, 20d * NOTIONAL * EPS)); } public void test_presentValueProductSensitivity_noExcoupon() { PointSensitivityBuilder point = PRICER.presentValueSensitivity(PRODUCT_NO_EXCOUPON, PROVIDER); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity(PROVIDER, (p) -> PRICER.presentValue(PRODUCT_NO_EXCOUPON, (p))); assertTrue(computed.equalWithTolerance(expected, 30d * NOTIONAL * EPS)); } public void test_presentValueSensitivityWithZSpread_continuous_noExcoupon() { PointSensitivityBuilder point = PRICER.presentValueSensitivityWithZSpread( PRODUCT_NO_EXCOUPON, PROVIDER, Z_SPREAD, CONTINUOUS, 0); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> PRICER.presentValueWithZSpread(PRODUCT_NO_EXCOUPON, (p), Z_SPREAD, CONTINUOUS, 0)); assertTrue(computed.equalWithTolerance(expected, 20d * NOTIONAL * EPS)); } public void test_presentValueSensitivityWithZSpread_periodic_noExcoupon() { PointSensitivityBuilder point = PRICER.presentValueSensitivityWithZSpread( PRODUCT_NO_EXCOUPON, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> PRICER.presentValueWithZSpread( PRODUCT_NO_EXCOUPON, (p), Z_SPREAD, PERIODIC, PERIOD_PER_YEAR)); assertTrue(computed.equalWithTolerance(expected, 20d * NOTIONAL * EPS)); } public void test_dirtyPriceSensitivity() { PointSensitivityBuilder point = PRICER.dirtyPriceSensitivity(BOND_SECURITY, PROVIDER); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> CurrencyAmount.of(EUR, PRICER.dirtyPriceFromCurves(BOND_SECURITY, (p)))); assertTrue(computed.equalWithTolerance(expected, NOTIONAL * EPS)); } public void test_dirtyPriceSensitivityWithZspread_continuous() { PointSensitivityBuilder point = PRICER.dirtyPriceSensitivityWithZspread(BOND_SECURITY, PROVIDER, Z_SPREAD, CONTINUOUS, 0); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> CurrencyAmount.of( EUR, PRICER.dirtyPriceFromCurvesWithZSpread( BOND_SECURITY, (p), Z_SPREAD, CONTINUOUS, 0))); assertTrue(computed.equalWithTolerance(expected, NOTIONAL * EPS)); } public void test_dirtyPriceSensitivityWithZspread_periodic() { PointSensitivityBuilder point = PRICER.dirtyPriceSensitivityWithZspread( BOND_SECURITY, PROVIDER, Z_SPREAD, PERIODIC, PERIOD_PER_YEAR); CurveCurrencyParameterSensitivities computed = PROVIDER.curveParameterSensitivity(point.build()); CurveCurrencyParameterSensitivities expected = FD_CAL.sensitivity( PROVIDER, (p) -> CurrencyAmount.of( EUR, PRICER.dirtyPriceFromCurvesWithZSpread( BOND_SECURITY, (p), Z_SPREAD, PERIODIC, PERIOD_PER_YEAR))); assertTrue(computed.equalWithTolerance(expected, NOTIONAL * EPS)); } // ------------------------------------------------------------------------- public void test_accruedInterest() { // settle before start LocalDate settleDate1 = START_DATE.minusDays(5); double accruedInterest1 = PRICER.accruedInterest(PRODUCT, settleDate1); assertEquals(accruedInterest1, 0d); // settle between endDate and endDate -lag LocalDate settleDate2 = date(2015, 10, 8); double accruedInterest2 = PRICER.accruedInterest(PRODUCT, settleDate2); assertEquals(accruedInterest2, -4.0 / 365.0 * FIXED_RATE * NOTIONAL, EPS); // normal LocalDate settleDate3 = date(2015, 4, 18); // not adjusted FixedCouponBond product = FixedCouponBond.builder() .dayCount(DAY_COUNT) .fixedRate(FIXED_RATE) .legalEntityId(ISSUER_ID) .currency(EUR) .notional(NOTIONAL) .periodicSchedule(PERIOD_SCHEDULE) .settlementDateOffset(DATE_OFFSET) .yieldConvention(YIELD_CONVENTION) .exCouponPeriod(DaysAdjustment.NONE) .build(); double accruedInterest3 = PRICER.accruedInterest(product, settleDate3); assertEquals(accruedInterest3, 6.0 / 365.0 * FIXED_RATE * NOTIONAL, EPS); } // ------------------------------------------------------------------------- /* US Street convention */ private static final LocalDate START_US = date(2006, 11, 15); private static final LocalDate END_US = START_US.plusYears(10); private static final PeriodicSchedule SCHEDULE_US = PeriodicSchedule.of( START_US, END_US, Frequency.P6M, BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, HolidayCalendars.SAT_SUN), StubConvention.SHORT_INITIAL, false); private static final FixedCouponBond PRODUCT_US = FixedCouponBond.builder() .dayCount(DayCounts.ACT_ACT_ICMA) .fixedRate(0.04625) .legalEntityId(ISSUER_ID) .currency(Currency.USD) .notional(100) .periodicSchedule(SCHEDULE_US) .settlementDateOffset(DaysAdjustment.ofBusinessDays(3, HolidayCalendars.SAT_SUN)) .yieldConvention(YieldConvention.US_STREET) .exCouponPeriod(DaysAdjustment.NONE) .build(); private static final LocalDate VALUATION_US = date(2011, 8, 18); private static final LocalDate SETTLEMENT_US = PRODUCT_US.getSettlementDateOffset().adjust(VALUATION_US); private static final LocalDate VALUATION_LAST_US = date(2016, 6, 3); private static final LocalDate SETTLEMENT_LAST_US = PRODUCT_US.getSettlementDateOffset().adjust(VALUATION_LAST_US); private static final double YIELD_US = 0.04; public void dirtyPriceFromYieldUS() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); assertEquals(dirtyPrice, 1.0417352500524246, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_US, SETTLEMENT_US, dirtyPrice); assertEquals(yield, YIELD_US, TOL); } public void dirtyPriceFromYieldUSLastPeriod() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); assertEquals(dirtyPrice, 1.005635683760684, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_US, SETTLEMENT_LAST_US, dirtyPrice); assertEquals(yield, YIELD_US, TOL); } public void modifiedDurationFromYieldUS() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); double price = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void modifiedDurationFromYieldUSLastPeriod() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); double price = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void convexityFromYieldUS() { double computed = PRICER.convexityFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); double duration = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void convexityFromYieldUSLastPeriod() { double computed = PRICER.convexityFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); double duration = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void macaulayDurationFromYieldUS() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_US, SETTLEMENT_US, YIELD_US); assertEquals(duration, 4.6575232098896215, TOL); // 2.x. } public void macaulayDurationFromYieldUSLastPeriod() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_US, SETTLEMENT_LAST_US, YIELD_US); assertEquals(duration, 0.43478260869565216, TOL); // 2.x. } /* UK BUMP/DMO convention */ private static final LocalDate START_UK = date(2002, 9, 7); private static final LocalDate END_UK = START_UK.plusYears(12); private static final PeriodicSchedule SCHEDULE_UK = PeriodicSchedule.of( START_UK, END_UK, Frequency.P6M, BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, HolidayCalendars.SAT_SUN), StubConvention.SHORT_INITIAL, false); private static final FixedCouponBond PRODUCT_UK = FixedCouponBond.builder() .dayCount(DayCounts.ACT_ACT_ICMA) .fixedRate(0.05) .legalEntityId(ISSUER_ID) .currency(Currency.GBP) .notional(100) .periodicSchedule(SCHEDULE_UK) .settlementDateOffset(DaysAdjustment.ofBusinessDays(1, HolidayCalendars.SAT_SUN)) .yieldConvention(YieldConvention.UK_BUMP_DMO) .exCouponPeriod( DaysAdjustment.ofCalendarDays( -7, BusinessDayAdjustment.of( BusinessDayConventions.PRECEDING, HolidayCalendars.SAT_SUN))) .build(); private static final LocalDate VALUATION_UK = date(2011, 9, 2); private static final LocalDate SETTLEMENT_UK = PRODUCT_UK.getSettlementDateOffset().adjust(VALUATION_UK); private static final LocalDate VALUATION_LAST_UK = date(2014, 6, 3); private static final LocalDate SETTLEMENT_LAST_UK = PRODUCT_UK.getSettlementDateOffset().adjust(VALUATION_LAST_UK); private static final double YIELD_UK = 0.04; public void dirtyPriceFromYieldUK() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); assertEquals(dirtyPrice, 1.0277859038905428, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_UK, SETTLEMENT_UK, dirtyPrice); assertEquals(yield, YIELD_UK, TOL); } public void dirtyPriceFromYieldUKLastPeriod() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); assertEquals(dirtyPrice, 1.0145736043763598, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_UK, SETTLEMENT_LAST_UK, dirtyPrice); assertEquals(yield, YIELD_UK, TOL); } public void modifiedDurationFromYieldUK() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); double price = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void modifiedDurationFromYieldUKLastPeriod() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); double price = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void convexityFromYieldUK() { double computed = PRICER.convexityFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); double duration = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void convexityFromYieldUKLastPeriod() { double computed = PRICER.convexityFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); double duration = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void macaulayDurationFromYieldUK() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_UK, SETTLEMENT_UK, YIELD_UK); assertEquals(duration, 2.8312260658609163, TOL); // 2.x. } public void macaulayDurationFromYieldUKLastPeriod() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_UK, SETTLEMENT_LAST_UK, YIELD_UK); assertEquals(duration, 0.25815217391304346, TOL); // 2.x. } /* German bond convention */ private static final LocalDate START_GER = date(2002, 9, 7); private static final LocalDate END_GER = START_GER.plusYears(12); private static final PeriodicSchedule SCHEDULE_GER = PeriodicSchedule.of( START_GER, END_GER, Frequency.P12M, BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, HolidayCalendars.SAT_SUN), StubConvention.SHORT_INITIAL, false); private static final FixedCouponBond PRODUCT_GER = FixedCouponBond.builder() .dayCount(DayCounts.ACT_ACT_ICMA) .fixedRate(0.05) .legalEntityId(ISSUER_ID) .currency(Currency.EUR) .notional(100) .periodicSchedule(SCHEDULE_GER) .settlementDateOffset(DaysAdjustment.ofBusinessDays(3, HolidayCalendars.SAT_SUN)) .yieldConvention(YieldConvention.GERMAN_BONDS) .exCouponPeriod(DaysAdjustment.NONE) .build(); private static final LocalDate VALUATION_GER = date(2011, 9, 2); private static final LocalDate SETTLEMENT_GER = PRODUCT_GER.getSettlementDateOffset().adjust(VALUATION_GER); private static final LocalDate VALUATION_LAST_GER = date(2014, 6, 3); private static final LocalDate SETTLEMENT_LAST_GER = PRODUCT_GER.getSettlementDateOffset().adjust(VALUATION_LAST_GER); private static final double YIELD_GER = 0.04; public void dirtyPriceFromYieldGerman() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); assertEquals(dirtyPrice, 1.027750910332271, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_GER, SETTLEMENT_GER, dirtyPrice); assertEquals(yield, YIELD_GER, TOL); } public void dirtyPriceFromYieldGermanLastPeriod() { double dirtyPrice = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); assertEquals(dirtyPrice, 1.039406595790844, TOL); // 2.x. double yield = PRICER.yieldFromDirtyPrice(PRODUCT_GER, SETTLEMENT_LAST_GER, dirtyPrice); assertEquals(yield, YIELD_GER, TOL); } public void modifiedDurationFromYieldGER() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); double price = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void modifiedDurationFromYieldGERLastPeriod() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); double price = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void convexityFromYieldGER() { double computed = PRICER.convexityFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); double duration = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void convexityFromYieldGERLastPeriod() { double computed = PRICER.convexityFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); double duration = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void macaulayDurationFromYieldGER() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_GER, SETTLEMENT_GER, YIELD_GER); assertEquals(duration, 2.861462874541554, TOL); // 2.x. } public void macaulayDurationFromYieldGERLastPeriod() { double duration = PRICER.macaulayDurationFromYield(PRODUCT_GER, SETTLEMENT_LAST_GER, YIELD_GER); assertEquals(duration, 0.26231286613148186, TOL); // 2.x. } /* Japan simple convention */ private static final LocalDate START_JP = date(2015, 9, 20); private static final LocalDate END_JP = START_JP.plusYears(10); private static final PeriodicSchedule SCHEDULE_JP = PeriodicSchedule.of( START_JP, END_JP, Frequency.P6M, BusinessDayAdjustment.of(BusinessDayConventions.FOLLOWING, HolidayCalendars.JPTO), StubConvention.SHORT_INITIAL, false); private static final double RATE_JP = 0.004; private static final FixedCouponBond PRODUCT_JP = FixedCouponBond.builder() .dayCount(DayCounts.NL_365) .fixedRate(RATE_JP) .legalEntityId(ISSUER_ID) .currency(Currency.JPY) .notional(100) .periodicSchedule(SCHEDULE_JP) .settlementDateOffset(DaysAdjustment.ofBusinessDays(3, HolidayCalendars.JPTO)) .yieldConvention(YieldConvention.JAPAN_SIMPLE) .exCouponPeriod(DaysAdjustment.NONE) .build(); private static final LocalDate VALUATION_JP = date(2015, 9, 24); private static final LocalDate SETTLEMENT_JP = PRODUCT_JP.getSettlementDateOffset().adjust(VALUATION_JP); private static final LocalDate VALUATION_LAST_JP = date(2025, 6, 3); private static final LocalDate SETTLEMENT_LAST_JP = PRODUCT_JP.getSettlementDateOffset().adjust(VALUATION_LAST_JP); private static final LocalDate VALUATION_ENDED_JP = date(2026, 8, 3); private static final LocalDate SETTLEMENT_ENDED_JP = PRODUCT_JP.getSettlementDateOffset().adjust(VALUATION_ENDED_JP); private static final double YIELD_JP = 0.00321; public void dirtyPriceFromYieldJP() { double computed = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP); double maturity = DayCounts.NL_365.relativeYearFraction(SETTLEMENT_JP, END_JP); double expected = PRICER.dirtyPriceFromCleanPrice( PRODUCT_JP, SETTLEMENT_JP, (1d + RATE_JP * maturity) / (1d + YIELD_JP * maturity)); assertEquals(computed, expected, TOL); double yield = PRICER.yieldFromDirtyPrice(PRODUCT_JP, SETTLEMENT_JP, computed); assertEquals(yield, YIELD_JP, TOL); } public void dirtyPriceFromYieldJPLastPeriod() { double computed = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP); double maturity = DayCounts.NL_365.relativeYearFraction(SETTLEMENT_LAST_JP, END_JP); double expected = PRICER.dirtyPriceFromCleanPrice( PRODUCT_JP, SETTLEMENT_LAST_JP, (1d + RATE_JP * maturity) / (1d + YIELD_JP * maturity)); assertEquals(computed, expected, TOL); double yield = PRICER.yieldFromDirtyPrice(PRODUCT_JP, SETTLEMENT_LAST_JP, computed); assertEquals(yield, YIELD_JP, TOL); } public void dirtyPriceFromYieldJPEnded() { double computed = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_ENDED_JP, YIELD_JP); assertEquals(computed, 0d, TOL); } public void modifiedDurationFromYielddJP() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP); double price = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void modifiedDurationFromYieldJPLastPeriod() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP); double price = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP); double priceUp = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP + EPS); double priceDw = PRICER.dirtyPriceFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP - EPS); double expected = 0.5 * (priceDw - priceUp) / price / EPS; assertEquals(computed, expected, EPS); } public void modifiedDurationFromYielddJPEnded() { double computed = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_ENDED_JP, YIELD_JP); assertEquals(computed, 0d, EPS); } public void convexityFromYieldJP() { double computed = PRICER.convexityFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP); double duration = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void convexityFromYieldJPLastPeriod() { double computed = PRICER.convexityFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP); double duration = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP); double durationUp = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP + EPS); double durationDw = PRICER.modifiedDurationFromYield(PRODUCT_JP, SETTLEMENT_LAST_JP, YIELD_JP - EPS); double expected = 0.5 * (durationDw - durationUp) / EPS + duration * duration; assertEquals(computed, expected, EPS); } public void convexityFromYieldJPEnded() { double computed = PRICER.convexityFromYield(PRODUCT_JP, SETTLEMENT_ENDED_JP, YIELD_JP); assertEquals(computed, 0d, EPS); } public void macaulayDurationFromYieldYieldJP() { assertThrows( () -> PRICER.macaulayDurationFromYield(PRODUCT_JP, SETTLEMENT_JP, YIELD_JP), UnsupportedOperationException.class, "The convention JAPAN_SIMPLE is not supported."); } }