/** * Copyright (C) 2014 - present by OpenGamma Inc. and the OpenGamma group of companies * * Please see distribution for license. */ package com.opengamma.strata.pricer.impl.credit.isda; import java.time.LocalDate; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.opengamma.strata.basics.date.DayCount; import com.opengamma.strata.basics.date.DayCounts; import com.opengamma.strata.collect.ArgChecker; /** * Simple analytic representation of a fixed coupon bond that allows it to be prices * consistently with a CDS (i.e. using the ISDA model) . */ public class BondAnalytic { private static final Logger log = LoggerFactory.getLogger(BondAnalytic.class); private static final DayCount ACT_365 = DayCounts.ACT_365F; private final double[] _paymentTimes; private final double[] _paymentAmounts; private final int _nPayments; private final double _recoveryRate; private final double _accuredInterest; /** * Simple analytic representation of a fixed coupon bond that allows it to be priced consistently * with a CDS (i.e. using the ISDA model). * * @param today the date today * @param coupon the bond coupon (as a fraction), can be zero * @param schedule the accrual start and end dates, and the payment dates * @param recoveryRate the expected recovery rate for the bond (this should be the same as that use for the CDS) * @param accrualDCC the day count used to calculate the length of an accrual period and thus the amount of the payments */ public BondAnalytic(LocalDate today, double coupon, IsdaPremiumLegSchedule schedule, double recoveryRate, DayCount accrualDCC) { this(today, coupon, schedule, recoveryRate, accrualDCC, ACT_365); } /** * Simple analytic representation of a fixed coupon bond that allows it to be priced consistently * with a CDS (i.e. using the ISDA model). * * @param today the date today * @param coupon the bond coupon (as a fraction), can be zero * @param schedule the accrual start and end dates, and the payment dates * @param recoveryRate the expected recovery rate for the bond (this should be the same as that use for the CDS) * @param accrualDCC the day count used to calculate the length of an accrual period and thus the amount of the payments * @param curveDCC the day count used on curve (NOTE ISDA uses ACT/365 (fixed) and it is not recommended to change this) */ public BondAnalytic( LocalDate today, double coupon, IsdaPremiumLegSchedule schedule, double recoveryRate, DayCount accrualDCC, DayCount curveDCC) { ArgChecker.notNull(today, "today"); ArgChecker.isTrue(coupon >= 0.0, "coupon is negative"); if (coupon > 1.0) { log.warn("coupon should be given as fraction. Value of " + coupon + "seems high."); } ArgChecker.notNull(schedule, "schedule"); ArgChecker.isTrue(recoveryRate >= 0.0 && recoveryRate <= 1.0, "recovery rate must be in range 0 - 1. value gives: ", recoveryRate); ArgChecker.notNull(accrualDCC, "accrualDCC"); ArgChecker.notNull(curveDCC, "curveDCC"); IsdaPremiumLegSchedule tSch = schedule.truncateSchedule(today); _nPayments = tSch.getNumPayments(); _paymentTimes = new double[_nPayments]; _paymentAmounts = new double[_nPayments]; for (int i = 0; i < _nPayments; i++) { _paymentAmounts[i] = coupon * accrualDCC.yearFraction(schedule.getAccStartDate(i), schedule.getAccEndDate(i)); _paymentTimes[i] = curveDCC.yearFraction(today, schedule.getPaymentDate(i)); } _paymentAmounts[_nPayments - 1] += 1.0; _accuredInterest = coupon * accrualDCC.yearFraction(schedule.getAccStartDate(0), today); _recoveryRate = recoveryRate; } /** * Simple analytic representation of a fixed coupon bond that allows it to be priced consistently * with a CDS (i.e. using the ISDA model). * * @param paymentTimes the payment times, year fraction between today and the payment dates, * this should use the same year fraction as the ISDA curves (i.e. normally ACT/365F) * @param paymentAmounts the actual payment amounts paid on the payment dates, the value should include the return of par * @param recoveryRate the expected recovery rate for the bond (this should be the same as that use for the CDS) * @param accuredInterest the amount of accrued interest on the bond today */ public BondAnalytic(double[] paymentTimes, double[] paymentAmounts, double recoveryRate, double accuredInterest) { ArgChecker.notEmpty(paymentTimes, "paymentTimes"); ArgChecker.notNull(paymentAmounts, "paymentAmounts"); _nPayments = paymentTimes.length; ArgChecker.isTrue(paymentAmounts.length == _nPayments, "number of payment times ({}) does not match number of payment amounts ({})", _nPayments, paymentAmounts.length); ArgChecker.isTrue(recoveryRate >= 0.0 && recoveryRate <= 1.0, "recovery rate must be in range 0 - 1. value gives: ", recoveryRate); ArgChecker.isTrue(accuredInterest >= 0.0, "accrued intrest must be give as positive"); ArgChecker.isTrue(paymentTimes[0] >= 0.0, "payments times must be positive. first value: ", paymentTimes[0]); for (int i = 1; i < _nPayments; i++) { ArgChecker.isTrue(paymentTimes[i] > paymentTimes[i - 1], "payment times must be assending"); } _paymentTimes = paymentTimes.clone(); _paymentAmounts = paymentAmounts.clone(); _recoveryRate = recoveryRate; _accuredInterest = accuredInterest; } //------------------------------------------------------------------------- /** * Gets the accrued interest. * * @return the accuredInterest */ public double getAccruedInterest() { return _accuredInterest; } /** * Gets the payment time for the given index. * * @param index the index * @return the paymentTime */ public double getPaymentTime(int index) { return _paymentTimes[index]; } /** * Gets the payment amount for the given index. * * @param index the index * @return the paymentAmount */ public double getPaymentAmount(int index) { return _paymentAmounts[index]; } /** * Gets the number of payments. * * @return the nPayments */ public int getnPayments() { return _nPayments; } /** * Gets the recovery rate. * * @return the recoveryRate */ public double getRecoveryRate() { return _recoveryRate; } }