/**
* Copyright (C) 2012 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.analytics.financial.credit.isdayieldcurve;
import java.util.ArrayList;
import org.threeten.bp.ZonedDateTime;
import com.opengamma.analytics.financial.credit.schedulegeneration.GenerateCreditDefaultSwapPremiumLegSchedule;
import com.opengamma.analytics.util.time.TimeCalculator;
import com.opengamma.financial.convention.businessday.BusinessDayConvention;
import com.opengamma.financial.convention.businessday.BusinessDayConventionFactory;
import com.opengamma.financial.convention.businessday.ModifiedFollowingBusinessDayConvention;
import com.opengamma.financial.convention.calendar.Calendar;
import com.opengamma.financial.convention.calendar.MondayToFridayCalendar;
import com.opengamma.financial.convention.daycount.DayCount;
import com.opengamma.financial.convention.daycount.DayCountFactory;
import com.opengamma.financial.convention.frequency.PeriodFrequency;
/**
*
*/
public class ISDAYieldCurve {
// ----------------------------------------------------------------------------------------------------------------------------------------
private static final DayCount ACT_365 = DayCountFactory.INSTANCE.getDayCount("ACT/365");
private static final DayCount ACT_360 = DayCountFactory.INSTANCE.getDayCount("ACT/360");
private static final Calendar holidayCalendar = new MondayToFridayCalendar("TestCalendar");
private static final BusinessDayConvention cashBusDayConv = BusinessDayConventionFactory.INSTANCE.getBusinessDayConvention("MF");
private static final ModifiedFollowingBusinessDayConvention modifiedFollowingBadDayConv = new ModifiedFollowingBusinessDayConvention();
private static final GenerateCreditDefaultSwapPremiumLegSchedule businessDayAdjuster = new GenerateCreditDefaultSwapPremiumLegSchedule();
// ----------------------------------------------------------------------------------------------------------------------------------------
// TODO : Add hashcode and equals methods
// TODO : Add argument checkers
// TODO : Check that nCash, nSwap != 0
// TODO : _basis is hard coded - replace this
// TODO : Add the swap code
// TODO : Check the efficiacy of the input data
// TODO : Need to bda the baseDate
// ----------------------------------------------------------------------------------------------------------------------------------------
// The total number of instruments used to construct the yield curve
private final int _numberOfInstruments;
// The number of money market (cash) instruments
private final int _numberOfCashInstruments;
// The number of swap instruments
private final int _numberOfSwapInstruments;
// This is the valuation date of the trade
private final ZonedDateTime _valuationDate;
// The base date is the trade valuation date + spot days (on this date the discount factor is 1) - this date is calculated inside this class
private final ZonedDateTime _baseDate;
// The number of spot days
private final int _spotDays;
// Given the tenors of the input instruments, compute their business day adjusted dates
private final ZonedDateTime[] _yieldCurveDates;
// The dates of the money market instruments
private final ZonedDateTime[] _cashDates;
// The dates of the swap instruments
private final ZonedDateTime[] _swapDates;
// The rates of the money market instruments
private final double[] _cashRates;
// The rates of the swap instruments
private final double[] _swapRates;
//private final ZonedDateTime[] _zCurveDates;
private final double[] _zCurveRates;
private final double[] _zCurveCCRates;
// An ArrayList to hold all the dates on the discount factor curve generated by the curve construction methodology
private final ArrayList<ZonedDateTime> _zCurveDates;
// An ArrayList to hold all the continuously compounded on the discount factor curve on the dates generated by the curve construction methodology
private final ArrayList<Double> _zCurveContinuouslyCompoundedRates;
private final ArrayList<Double> _zCurveDiscountFactors;
// NOTE : This is hard coded for the moment (is a fixed input in the ISDA methodology)
private final double _basis = 1.0;
// Fixed swap interval is 6M (2 payments per year)
private final double _fixedFreq = 2.0;
private final int _fixedInterval = 6;
// Float swap interval is 3M (4 payments per year)
private final double _floatFreq = 4.0;
private final int _floatInterval = 3;
// ----------------------------------------------------------------------------------------------------------------------------------------
public ISDAYieldCurve(
final ZonedDateTime valuationDate,
final ISDAYieldCurveTenors[] instrumentTenors,
final ISDAInstrumentTypes[] instrumentTypes,
final double[] instrumentRates,
final int spotDays,
final DayCount moneyMarketDaycountConvention,
final DayCount swapDaycountDaycountConvention,
final DayCount floatDaycountConvention,
final PeriodFrequency swapInterval,
final PeriodFrequency floatInterval,
final BusinessDayConvention badDayConvention) {
// ----------------------------------------------------------------------------------------------------------------------------------------
// Add argument checkers (num instruments != 0, swap tenors in ascending order, rates >= -100%, rates == 0.0, tenors after baseDate etc)
// ----------------------------------------------------------------------------------------------------------------------------------------
_valuationDate = valuationDate;
_spotDays = spotDays;
_baseDate = valuationDate.plusDays(_spotDays);
// Determine the total number of instruments used to construct the yield curve
_numberOfInstruments = instrumentTenors.length;
// Determine the business day adjusted dates of the instruments (remember the input is not dates, but tenors e.g. 1Y)
_yieldCurveDates = calculateInstrumentDates(_baseDate, instrumentTypes, instrumentTenors, holidayCalendar);
// Determine the number of swap instruments in the input list of instruments
_numberOfSwapInstruments = getNumberOfInstrumentsOfSpecificType(instrumentTypes, ISDAInstrumentTypes.Swap);
// Determine the number of money market instruments in the input list of instruments
_numberOfCashInstruments = getNumberOfInstrumentsOfSpecificType(instrumentTypes, ISDAInstrumentTypes.MoneyMarket);
// Get the dates of the money market instrument maturities
_cashDates = getInstrumentDates(_yieldCurveDates, instrumentTypes, ISDAInstrumentTypes.MoneyMarket, _numberOfCashInstruments);
// Get the rates of the money market instruments
_cashRates = getInstrumentRates(instrumentRates, instrumentTypes, ISDAInstrumentTypes.MoneyMarket, _numberOfCashInstruments);
// Get the dates of the swap instrument maturities
_swapDates = getInstrumentDates(_yieldCurveDates, instrumentTypes, ISDAInstrumentTypes.Swap, _numberOfSwapInstruments);
// Get the rates of the swap instruments
_swapRates = getInstrumentRates(instrumentRates, instrumentTypes, ISDAInstrumentTypes.Swap, _numberOfSwapInstruments);
//_zCurveDates = new ZonedDateTime[_numberOfInstruments];
_zCurveRates = new double[_numberOfInstruments];
_zCurveCCRates = new double[_numberOfInstruments];
final double[] zcCashDates = new double[_numberOfCashInstruments + 2];
final double[] zcCashRates = new double[_numberOfCashInstruments + 2];
final double[] zcCashDiscount = new double[_numberOfCashInstruments + 2];
// Initialise the ArrayList to hold the dates on the curve
_zCurveDates = new ArrayList<ZonedDateTime>();
// Initialise the ArrayList to hold the continuously compounded rates on the curve
_zCurveContinuouslyCompoundedRates = new ArrayList<Double>();
_zCurveDiscountFactors = new ArrayList<Double>();
// ----------------------------------------------------------------------
// Add the MM instruments
for (int i = 0; i < _numberOfCashInstruments; i++) {
// TODO : Check if rate <= 0
final ZonedDateTime startDate = _baseDate;
final ZonedDateTime endDate = _cashDates[i];
final double rateYF = TimeCalculator.getTimeBetween(startDate, endDate, ACT_360);
// TODO : Check denom != 0
final double denom = 1.0 + _cashRates[i] * rateYF;
zcCashDiscount[i] = 1.0 / denom;
final double rateYF2 = TimeCalculator.getTimeBetween(startDate, endDate, ACT_365);
zcCashRates[i] = Math.pow(zcCashDiscount[i], -1.0 / (_basis * rateYF2)) - 1.0;
//_zCurveDates[i] = _cashDates[i];
_zCurveRates[i] = _cashRates[i];
_zCurveCCRates[i] = zcCashRates[i];
_zCurveDates.add(_cashDates[i]);
_zCurveContinuouslyCompoundedRates.add(_zCurveCCRates[i]);
_zCurveDiscountFactors.add(zcCashDiscount[i]);
}
final ZonedDateTime lastMMDate = _cashDates[_cashDates.length - 1];
// ----------------------------------------------------------------------
// Add the swap instruments
// TODO : Check is numSwaps == 0
// -----------------------------------
// Compute the last stub date
ZonedDateTime lastStubDate;
if (_numberOfCashInstruments < 1) {
lastStubDate = _baseDate;
} else {
lastStubDate = _cashDates[_numberOfCashInstruments - 1];
}
int offset = 0;
int numSwaps = _numberOfSwapInstruments;
// TODO : Remember to check this logic when some of the swap tenors lie before the end of the deposit tenors (don't add these swaps to the yield curve)
while (numSwaps > 0 && _swapDates[offset].isBefore(lastStubDate)) {
offset++;
numSwaps--;
}
// TODO : Remember to place the following code within this loop - not important for the moment
if (numSwaps > 0) {
}
// -----------------------------------
final ZonedDateTime[] swapDatesOriginal = new ZonedDateTime[_numberOfSwapInstruments]; // The unadjusted maturity date of the swap
final ZonedDateTime[] swapDatesAdjusted = new ZonedDateTime[_numberOfSwapInstruments]; // The adjusted maturity date of the swap
final ZonedDateTime[] swapDatesPrevious = new ZonedDateTime[_numberOfSwapInstruments]; // The cashflow prior to the maturity of the swap
final boolean[] swapDatesOnCycle = new boolean[_numberOfSwapInstruments];
// -----------------------------------
for (int i = 0; i < _numberOfSwapInstruments; i++) {
swapDatesOriginal[i] = _swapDates[i];
swapDatesAdjusted[i] = modifiedFollowingBadDayConv.adjustDate(holidayCalendar, _swapDates[i]);
swapDatesOnCycle[i] = onCycle(_baseDate, swapDatesOriginal[i]);
swapDatesPrevious[i] = previousDate(swapDatesOnCycle[i], _baseDate, swapDatesOriginal[i]);
}
// -----------------------------------
final double[] swapRates = _swapRates;
final boolean oneAlreadyAdded = false;
final double[] zc = new double[_numberOfSwapInstruments];
for (int i = 0; i < _numberOfSwapInstruments; i++) {
if (swapDatesAdjusted[i].isAfter(lastMMDate/*tempZCDates[_numberOfCashInstruments - 1]*/))
{
if (oneAlreadyAdded &&
swapRates[i - 1] != 0.0 &&
swapDatesAdjusted[i - 1].equals(lastMMDate/*tempZCDates[_numberOfCashInstruments - 1]*/) &&
swapDatesPrevious[i - 1].equals(swapDatesOriginal[i - 1]) &&
swapDatesOnCycle[i]) {
// TODO : Add code
} else {
zc[i] = JpmcsdsZCAddSwap(1.0, swapDatesOriginal[i], swapDatesOnCycle[i], swapRates[i], lastMMDate);
}
} // end if
} // end for over i
// -----------------------------------
// TODO : Remember need to have two valuation dates that exercise the onCycle code logic i.e. <= 28th month valuation date
// TODO : Remember need to exercise the oneAlreadyAdded code logic
// TODO : Remember to take into account if we only have swaps in the input data list
// ----------------------------------------------------------------------------------------------------------------------------------------
}
private double JpmcsdsZCAddSwap(final double price, final ZonedDateTime matDate, final boolean onCycle, final double rate, final ZonedDateTime lastMMDate) {
final double Z = 0;
// TODO : Check 'If floating side at par' condition - suspect discZC is always null
boolean isEndStub = false;
if (onCycle) {
isEndStub = true;
} else {
// TODO : Check that the interval is the correct sign
final int[] temp = JpmcdsCountDates(_baseDate, matDate, 1);
final int numIvls = temp[0];
final int extraDays = temp[1];
if (extraDays > 0) {
isEndStub = false;
} else {
isEndStub = true;
}
}
// Calc cfl
if (rate == 0.0) {
// TODO : Add this code
}
// ---------------------
// cfl = JpmcdsZCGetSwapCFL
final ZonedDateTime[] dl = JpmcdsZCGetSwapCouponDL(_baseDate, matDate, isEndStub);
final ZonedDateTime[] cflDate = new ZonedDateTime[dl.length];
final double[] cflAmount = new double[dl.length];
ZonedDateTime prevDate = _baseDate;
for (int i = 0; i < dl.length; i++) {
// TODO : Need to sort out what day count conv is being used
final double yearFraction = 0.5; //TimeCalculator.getTimeBetween(prevDate, dl[i], ACT_365);
cflDate[i] = dl[i];
cflAmount[i] = rate * yearFraction;
prevDate = dl[i];
}
cflAmount[cflAmount.length - 1] += 1.0;
// ---------------------
// Adjust matDate
final ZonedDateTime adjMatDate = modifiedFollowingBadDayConv.adjustDate(holidayCalendar, matDate);
/* Add rate implied by cashflow list to the zeroCurve.
*/
JpmcdsZCAddCashFlowList(cflDate, cflAmount, price, adjMatDate, lastMMDate);
// TODO : cCheck if date == 0L and if there are no MM deposits already used to build the zc curve
return Z;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private void JpmcdsZCAddCashFlowList(final ZonedDateTime[] cflDate, final double[] cflAmount, final double price, final ZonedDateTime date, final ZonedDateTime lastMMDate) {
int firstUncovered = 0;
double sumNPV = 0.0;
// TODO : cCheck if date == 0L and if there are no MM deposits already used to build the zc curve
final ZonedDateTime lastZCDate = lastMMDate;
// TODO : Check if date <= lastZCDate
firstUncovered = cflDate.length - 1;
// TODO : Check if firstUncovered < 0 || cflDate[firstUncovered].isBefore(lastZCDate)
while (firstUncovered >= 0 && cflDate[firstUncovered].isAfter(lastZCDate)) {
firstUncovered--;
}
firstUncovered++;
if (firstUncovered > 0) {
sumNPV = JpmcdsJpmcdsZCPresentValueCFL(cflDate, cflAmount, 0, firstUncovered - 1);
}
if (firstUncovered == cflDate.length - 1 && cflDate[firstUncovered].equals(date)) {
double discFactor = 0.0;
final double pvOfLast = price - sumNPV;
double futOfLast = 0.0;
futOfLast = cflAmount[firstUncovered];
// TODO : Check if futOfLast == 0.0 || pvOfLat == 0.0;
discFactor = pvOfLast / futOfLast;
// TODO : Add JpmcdsZCAddDiscountFactor
} else if (firstUncovered < cflDate.length) {
double rate = 0.0;
// TODO : If no MM instruments guess rate = 6%
rate = JpmcdsZCInterpolate(date);
rate = Math.max(0.01, Math.min(1.0, rate));
}
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private int[] JpmcdsBinarySearch(final ZonedDateTime date) {
final int[] results = new int[2];
final int N = _zCurveDates.size();
final int shiftValue = 2;
if (!date.isAfter(_zCurveDates.get(0))) {
results[0] = 0;
results[1] = 1;
return results;
}
if (!date.isBefore(_zCurveDates.get(N - 1))) {
results[0] = N - 2;
results[1] = N - 1;
return results;
}
int mid = 0;
int lo = 0;
int hi = N - 2;
for (int count = N + 1; count > 0; count--) {
mid = (hi + lo) / 2;
if (date.isBefore(_zCurveDates.get(mid))) {
hi = mid - 1;
} else if (date.isAfter(_zCurveDates.get(mid + 1))) {
lo = mid + 1;
} else {
break;
}
}
lo = mid;
hi = mid + 1;
while (_zCurveDates.get(lo).equals(_zCurveDates.get(hi))) {
hi++;
}
results[0] = lo;
results[1] = hi;
return results;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private double JpmcdsZCInterpolate(final ZonedDateTime date) {
double rate = 0.0;
int lo = 0;
int hi = 0;
// TODO : Need to replace with ACT / 365.25
final double yearsFromBaseDate = TimeCalculator.getTimeBetween(_baseDate, date, ACT_365) * (365 / 365.25);
// TODO : Check if there is nothing in the zero curve
// TODO : Check if date == _basedate
if (!date.isAfter(_zCurveDates.get(0))) {
rate = _zCurveContinuouslyCompoundedRates.get(0);
}
final int n = _zCurveDates.size();
if (date.isAfter(_zCurveDates.get(n - 1))) {
}
final int[] temp = JpmcdsBinarySearch(date);
lo = temp[0];
hi = temp[1];
if (_zCurveDates.get(lo).equals(date)) {
rate = _zCurveContinuouslyCompoundedRates.get(lo);
}
if (_zCurveDates.get(hi).equals(date)) {
rate = _zCurveContinuouslyCompoundedRates.get(hi);
}
return rate;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private double JpmcdsJpmcdsZCPresentValueCFL(final ZonedDateTime[] cflDate, final double[] cflAmount, final int iLo, final int iHi) {
int j = 0;
double sumPV = 0.0;
// TODO : Error checking on iLo and iHi
final int numItems = _zCurveDates.size();
for (int i = iLo; i <= iHi; i++) {
final double amt = cflAmount[i];
final ZonedDateTime date = cflDate[i];
double pv = 0.0;
//ZonedDateTime zcDate = _zCurveDates.get(j);
while (j < numItems && _zCurveDates.get(j).isBefore(date)) {
j++;
}
if (j < numItems && _zCurveDates.get(j).equals(date)) {
final double discount = _zCurveDiscountFactors.get(j);
pv = discount * amt;
} else {
pv = 0.0; // JpmcdsZCPresentValue
}
sumPV += pv;
}
return sumPV;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private ZonedDateTime[] JpmcdsZCGetSwapCouponDL(final ZonedDateTime valueDate, final ZonedDateTime matDate, final boolean stubAtEnd) {
int[] temp = new int[2];
if (stubAtEnd) {
temp = JpmcdsCountDates(_baseDate, matDate, 1);
} else {
temp = JpmcdsCountDates(matDate, _baseDate, -1);
}
int numDates;
final int numIntervals = temp[0];
final int extraDays = temp[1];
if (extraDays > 0) {
numDates = numIntervals + 2;
} else {
numDates = numIntervals + 1;
}
ZonedDateTime[] dl = new ZonedDateTime[numDates];
if (stubAtEnd) {
dl = JpmcdsMakeTDateArray(valueDate, 0, 1, numDates - 1);
dl[numDates - 1] = matDate;
} else {
// TODO : Careful about this - is it meant to fill in the array from the back?
// TODO : Need to write this
dl = JpmcdsMakeTDateArray(matDate, 0, -1, numDates - 1);
dl[0] = valueDate;
}
final ZonedDateTime[] payDates = new ZonedDateTime[numDates - 1];
final ZonedDateTime[] bdaPayDates = new ZonedDateTime[numDates - 1];
for (int idx = 0; idx < numDates - 1; idx++) {
payDates[idx] = dl[idx + 1];
}
for (int idx = 0; idx < payDates.length; idx++) {
bdaPayDates[idx] = modifiedFollowingBadDayConv.adjustDate(holidayCalendar, payDates[idx]);
}
// numItems--
return bdaPayDates;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private ZonedDateTime[] JpmcdsMakeTDateArray(final ZonedDateTime baseDate, final int startIdx, final int arrayIncrement, final int nDates) {
final ZonedDateTime[] dateArray = new ZonedDateTime[nDates + 1];
for (int idx = 0; idx < nDates; idx++) {
final int offsetInterval = _fixedInterval * (startIdx + idx);
dateArray[idx * arrayIncrement] = baseDate.plusMonths(offsetInterval);
}
return dateArray;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private int[] JpmcdsCountDates(final ZonedDateTime fromDate, final ZonedDateTime toDate, final int k) {
final int[] result = new int[2];
final double intervalYears = _fixedInterval / 12.0;
final double fromToYears = TimeCalculator.getTimeBetween(fromDate, toDate, ACT_365);
final int lowNumIntervals = Math.max(0, (int) Math.floor(Math.abs(fromToYears / intervalYears)) - 2);
int index = lowNumIntervals;
ZonedDateTime currDate = fromDate.plusMonths(k * index * _fixedInterval);
ZonedDateTime lastDate = currDate;
while (!currDate.isBefore(fromDate) && !currDate.isAfter(toDate)) {
++index;
lastDate = currDate;
currDate = fromDate.plusMonths(k * index * _fixedInterval);
}
// TODO : Check if this is < lowNumIntervals
result[0] = index - 1;
final long t = toDate.toEpochSecond() - lastDate.toEpochSecond();
result[1] = (int) (t / (60 * 60));
return result;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private boolean onCycle(final ZonedDateTime valueDate, final ZonedDateTime origDate) {
boolean onCycle = false;
if (valueDate.getDayOfMonth() <= 28 && origDate.getDayOfMonth() <= 28) {
// TODO : Add this code JpmcdsCountDates
} else {
onCycle = false;
}
return onCycle;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private ZonedDateTime previousDate(final boolean onCycle, final ZonedDateTime valueDate, final ZonedDateTime origDate) {
ZonedDateTime prevDate = origDate;
if (onCycle) {
// TODO : Add this code
} else {
prevDate = origDate.minusMonths(_fixedInterval);
}
return prevDate;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private ZonedDateTime[] calculateInstrumentDates(
final ZonedDateTime baseDate,
final ISDAInstrumentTypes[] instrumentTypes,
final ISDAYieldCurveTenors[] yieldCurveTenors,
final Calendar calendar) {
final ZonedDateTime[] yieldCurveDates = new ZonedDateTime[yieldCurveTenors.length];
final ZonedDateTime[] bdaYieldCurveDates = new ZonedDateTime[yieldCurveTenors.length];
for (int i = 0; i < yieldCurveTenors.length; i++) {
// This is very hacky - there must be a better way of doing this probably a switch (ask EM)
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._1M) {
yieldCurveDates[i] = baseDate.plusMonths(1);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._2M) {
yieldCurveDates[i] = baseDate.plusMonths(2);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._3M) {
yieldCurveDates[i] = baseDate.plusMonths(3);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._6M) {
yieldCurveDates[i] = baseDate.plusMonths(6);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._9M) {
yieldCurveDates[i] = baseDate.plusMonths(9);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._1Y) {
yieldCurveDates[i] = baseDate.plusMonths(12);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._2Y) {
yieldCurveDates[i] = baseDate.plusYears(2);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._3Y) {
yieldCurveDates[i] = baseDate.plusYears(3);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._4Y) {
yieldCurveDates[i] = baseDate.plusYears(4);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._5Y) {
yieldCurveDates[i] = baseDate.plusYears(5);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._6Y) {
yieldCurveDates[i] = baseDate.plusYears(6);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._7Y) {
yieldCurveDates[i] = baseDate.plusYears(7);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._8Y) {
yieldCurveDates[i] = baseDate.plusYears(8);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._9Y) {
yieldCurveDates[i] = baseDate.plusYears(9);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._10Y) {
yieldCurveDates[i] = baseDate.plusYears(10);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._12Y) {
yieldCurveDates[i] = baseDate.plusYears(12);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._15Y) {
yieldCurveDates[i] = baseDate.plusYears(15);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._20Y) {
yieldCurveDates[i] = baseDate.plusYears(20);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._25Y) {
yieldCurveDates[i] = baseDate.plusYears(25);
}
if (yieldCurveTenors[i] == ISDAYieldCurveTenors._30Y) {
yieldCurveDates[i] = baseDate.plusYears(30);
}
bdaYieldCurveDates[i] = yieldCurveDates[i];
// If the instrument is cash, then business day adjust this (don't bda the swap maturities)
if (instrumentTypes[i] == ISDAInstrumentTypes.MoneyMarket) {
bdaYieldCurveDates[i] = modifiedFollowingBadDayConv.adjustDate(calendar, yieldCurveDates[i]);
}
}
return bdaYieldCurveDates;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
public ZonedDateTime[] getCashDates() {
return _cashDates;
}
public ZonedDateTime[] getSwapDates() {
return _swapDates;
}
public double[] getCashRates() {
return _cashRates;
}
public double[] getSwapRates() {
return _swapRates;
}
public ZonedDateTime getBaseDate() {
return _baseDate;
}
public ZonedDateTime[] getYieldCurveDates() {
return _yieldCurveDates;
}
public int getNumberOfInstruments() {
return _numberOfInstruments;
}
public int getNumberOfCashInstruments() {
return _numberOfCashInstruments;
}
public int getNumberOfSwapInstruments() {
return _numberOfSwapInstruments;
}
/*
public ZonedDateTime[] getZCurveDates() {
return _zCurveDates;
}
*/
public ArrayList<ZonedDateTime> getZCurveDates() {
return _zCurveDates;
}
public ArrayList<Double> getZCurveContinuouslyCompoundedRates() {
return _zCurveContinuouslyCompoundedRates;
}
public ArrayList<Double> getZCurveDiscountFactor() {
return _zCurveDiscountFactors;
}
public double[] getZCurveRates() {
return _zCurveRates;
}
public double[] getZCurveCCRates() {
return _zCurveCCRates;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private int getNumberOfInstrumentsOfSpecificType(
final ISDAInstrumentTypes[] rateTypes,
final ISDAInstrumentTypes rateType) {
int nInstruments = 0;
for (final ISDAInstrumentTypes rateType2 : rateTypes) {
if (rateType2 == rateType) {
nInstruments++;
}
}
return nInstruments;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private ZonedDateTime[] getInstrumentDates(
final ZonedDateTime[] instrumentMaturities,
final ISDAInstrumentTypes[] rateTypes,
final ISDAInstrumentTypes rateType,
final int nInstruments) {
int index = 0;
final ZonedDateTime[] instrumentDates = new ZonedDateTime[nInstruments];
for (int i = 0; i < rateTypes.length; i++) {
if (rateTypes[i] == rateType) {
instrumentDates[index] = instrumentMaturities[i];
index++;
}
}
return instrumentDates;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private double[] getInstrumentRates(
final double[] instrumentRates,
final ISDAInstrumentTypes[] rateTypes,
final ISDAInstrumentTypes rateType,
final int nInstruments) {
int index = 0;
final double[] instrumentInputRates = new double[nInstruments];
for (int i = 0; i < rateTypes.length; i++) {
if (rateTypes[i] == rateType) {
instrumentInputRates[index] = instrumentRates[i];
index++;
}
}
return instrumentInputRates;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private static int getNumberOfActiveSwaps(
final ZonedDateTime lastStubDate,
final ZonedDateTime[] swapDates,
final int nSwap) {
int offset = 0;
int numSwaps = nSwap;
while (numSwaps > 0 && swapDates[offset].isBefore(lastStubDate)) {
offset++;
numSwaps--;
}
return numSwaps;
}
boolean calculateOnCycle(
final ZonedDateTime valueDate,
final ZonedDateTime unadjustedSwapDate,
final PeriodFrequency swapFixedLegCouponFrequency) {
boolean onCycle = false;
if (valueDate.getDayOfMonth() <= 28 && unadjustedSwapDate.getDayOfMonth() <= 28) {
final ZonedDateTime fromDate = valueDate;
final ZonedDateTime toDate = unadjustedSwapDate;
double intervalYears = 0.0;
final double fromToYears = TimeCalculator.getTimeBetween(fromDate, toDate, ACT_365);
// Need to fix this - bit of a hack
if (swapFixedLegCouponFrequency == PeriodFrequency.SEMI_ANNUAL) {
intervalYears = 0.5;
}
if (swapFixedLegCouponFrequency == PeriodFrequency.QUARTERLY) {
intervalYears = 0.25;
}
final int lowNumIntervals = Math.max(0, (int) Math.floor(Math.abs(fromToYears / intervalYears)) - 2);
int index = lowNumIntervals;
int compoundInterval = 0;
int multiplier = 0;
// Need to fix this - bit of a hack
if (swapFixedLegCouponFrequency == PeriodFrequency.SEMI_ANNUAL) {
multiplier = 6;
compoundInterval = index * multiplier;
}
if (swapFixedLegCouponFrequency == PeriodFrequency.QUARTERLY) {
multiplier = 3;
compoundInterval = index * multiplier;
}
ZonedDateTime currDate = valueDate.plusMonths(compoundInterval);
ZonedDateTime lastDate = currDate;
while (currDate.isAfter(fromDate) && !currDate.isAfter(toDate)) {
++index;
lastDate = currDate;
currDate = valueDate.plusMonths(index * multiplier);
}
final int numIntervals = index - 1;
final int extraDays = (int) Math.abs(TimeCalculator.getTimeBetween(toDate, lastDate));
if (extraDays == 0) {
onCycle = true;
}
}
return onCycle;
}
//Assuming that the inputs are ordered as MM followed by swap instruments
private void jpmCDSZCSwaps(
final ZonedDateTime valueDate,
final ZonedDateTime[] cashDates,
final ZonedDateTime[] swapDates,
final double[] swapRates,
final int numSwaps,
final double fixedSwapFreq,
final double floatSwapFreq,
final DayCount swapFixedLegDaycountFractionConvention,
final DayCount swapFloatingLegDaycountFractionConvention,
final BusinessDayConvention businessdayAdjustmentConvention,
final Calendar calendar) {
if (numSwaps == 0) {
return;
}
final GenerateCreditDefaultSwapPremiumLegSchedule swapMaturities = new GenerateCreditDefaultSwapPremiumLegSchedule();
final ZonedDateTime lastStubDate = cashDates[cashDates.length - 1];
// Need to implement this if want to have swaps with maturities less than MM instruments
// numSwaps = getNumberOfActiveSwaps(lastStubDate, swapDates, numSwaps);
final ZonedDateTime[] unadjustedSwapDates = new ZonedDateTime[swapDates.length];
final ZonedDateTime[] adjustedSwapDates = new ZonedDateTime[swapDates.length];
final boolean[] onCycleSwapDates = new boolean[swapDates.length];
final ZonedDateTime[] previousSwapDates = new ZonedDateTime[swapDates.length];
for (int i = 0; i < numSwaps; i++) {
unadjustedSwapDates[i] = swapDates[i];
adjustedSwapDates[i] = swapMaturities.businessDayAdjustDate(swapDates[i], calendar, businessdayAdjustmentConvention);
}
int numIntervals = 0;
int extraDays = 0;
int compoundInterval = 0;
int multiplier = 0;
double intervalYears = 0.0;
// Need to fix this - bit of a hack
/*if (swapFixedLegCouponFrequency == PeriodFrequency.SEMI_ANNUAL)*/
{
intervalYears = 0.5;
multiplier = 6;
}
/*if (swapFixedLegCouponFrequency == PeriodFrequency.QUARTERLY)*/
{
intervalYears = 0.25;
multiplier = 3;
}
// -------------------------------------------
for (int i = 0; i < numSwaps; i++) {
boolean onCycle = false;
if (valueDate.getDayOfMonth() <= 28 && unadjustedSwapDates[i].getDayOfMonth() <= 28) {
final ZonedDateTime fromDate = valueDate;
final ZonedDateTime toDate = unadjustedSwapDates[i];
final double fromToYears = TimeCalculator.getTimeBetween(fromDate, toDate, ACT_365);
final int lowNumIntervals = Math.max(0, (int) Math.floor(Math.abs(fromToYears / intervalYears)) - 2);
int index = lowNumIntervals;
compoundInterval = index * multiplier;
ZonedDateTime currDate = fromDate.plusMonths(compoundInterval);
ZonedDateTime lastDate = currDate;
while (currDate.isAfter(fromDate) && !currDate.isAfter(toDate)) {
++index;
lastDate = currDate;
currDate = valueDate.plusMonths(index * multiplier);
}
numIntervals = index - 1;
extraDays = (int) Math.abs(TimeCalculator.getTimeBetween(toDate, lastDate));
if (extraDays == 0) {
onCycle = true;
} // end if extraDays
} // end if dom <= 28
onCycleSwapDates[i] = onCycle;
ZonedDateTime prevDate;
if (onCycleSwapDates[i]) {
prevDate = valueDate.plusMonths(multiplier * (numIntervals - 1));
} else {
prevDate = unadjustedSwapDates[i].plusMonths(6 * (-1));
}
previousSwapDates[i] = prevDate;
} // end loop over i
// -------------------------------------------
final boolean oneAlreadyAdded = false;
boolean isEndStub = false;
int numDates;
for (int i = 0; i < numSwaps; i++) {
if (adjustedSwapDates[i].isAfter(cashDates[cashDates.length - 1])) {
if (onCycleSwapDates[i]) {
isEndStub = true;
} else {
// Need to fill this in - jpmcdsisendstub
} // end if
// need to add rate = 0 case
if (isEndStub) {
final ZonedDateTime fromDate = valueDate;
final ZonedDateTime toDate = unadjustedSwapDates[i];
final double fromToYears = TimeCalculator.getTimeBetween(fromDate, toDate, ACT_365);
final int lowNumIntervals = Math.max(0, (int) Math.floor(Math.abs(fromToYears / intervalYears)) - 2);
int index = lowNumIntervals;
compoundInterval = index * multiplier;
ZonedDateTime currDate = fromDate.plusMonths(compoundInterval);
ZonedDateTime lastDate = currDate;
while (currDate.isAfter(fromDate) && !currDate.isAfter(toDate)) {
++index;
lastDate = currDate;
currDate = valueDate.plusMonths(index * multiplier);
}
numIntervals = index - 1;
extraDays = (int) Math.abs(TimeCalculator.getTimeBetween(toDate, lastDate));
} else {
// Need to add this
}
if (extraDays > 0) {
numDates = numIntervals + 2;
} else {
numDates = numIntervals + 1;
}
final ZonedDateTime[] dateList = new ZonedDateTime[numDates];
if (isEndStub) {
for (int j = 0; j < numDates - 1; j++) {
dateList[j] = valueDate.plusMonths(j * multiplier);
}
dateList[numDates - 1] = unadjustedSwapDates[i];
} else {
// Need to add this
}
for (int j = 0; j < numDates - 1; j++) {
dateList[j] = dateList[j + 1];
}
numDates--;
//
final ZonedDateTime[] adjustedDateList = new ZonedDateTime[numDates];
for (int idx = 0; idx < numDates; idx++) {
adjustedDateList[idx] = swapMaturities.businessDayAdjustDate(dateList[idx], calendar, businessdayAdjustmentConvention);
}
final double[] cashflowList = new double[numDates];
ZonedDateTime prevDate = valueDate;
for (int idx = 0; idx < numDates; idx++) {
final ZonedDateTime cDate = adjustedDateList[idx];
final double dcf = ACT_360.getDayCountFraction(prevDate, cDate);
cashflowList[idx] = dcf * swapRates[i];
prevDate = cDate;
}
cashflowList[numDates - 1] += 1.0;
final ZonedDateTime adjMatDate = adjustedSwapDates[i];
final double price = 1.0;
//ZCAddCashFlowList(adjustedDateList, cashflowList, price, adjMatDate);
} // end if adjdate >cashdate
} // end loop over i
}
// ----------------------------------------------------------------------------------------------------------------------------------------
private double getInterpolatedRate(
final double t,
final double t1,
final double t2,
final double z1,
final double z2) {
final double z1t1 = z1 * t1;
final double z2t2 = z2 * t2;
final double zt = z1t1 + (z2t2 - z1t1) * (t - t1) / (t2 - t1);
return zt;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
public final double getDiscountFactor(final ZonedDateTime valueDate, final ZonedDateTime date) {
// Hacking time - I am now at the point where I have left my senses behind
// TODO : Check that the size of the curve dates and curve rates lists are the same
final ZonedDateTime[] boxedTimePoints = new ZonedDateTime[_zCurveDates.size()];
final Double[] boxedCCRates = new Double[_zCurveContinuouslyCompoundedRates.size()];
_zCurveDates.toArray(boxedTimePoints);
_zCurveContinuouslyCompoundedRates.toArray(boxedCCRates);
final ZonedDateTime[] timePoints = new ZonedDateTime[boxedTimePoints.length];
final double[] ccRates = new double[boxedTimePoints.length];
for (int i = 0; i < boxedTimePoints.length; ++i) {
timePoints[i] = boxedTimePoints[i];
ccRates[i] = boxedCCRates[i];
}
ZonedDateTime loDate;
ZonedDateTime hiDate;
double loRate = 0.0;
double hiRate = 0.0;
double z1 = 0.0;
double z2 = 0.0;
double t1 = 0.0;
double t2 = 0.0;
double t = 0.0;
double Z = 0.0;
double rate = 0.0;
// ---------------------------------------------
// Extrapolation below the first date
if (date.isBefore(timePoints[0])) {
//loRate = _zCurveCCRates[0];
loRate = ccRates[0];
z1 = Math.log(1.0 + loRate);
t = TimeCalculator.getTimeBetween(valueDate, date, ACT_365);
rate = z1;
Z = Math.exp(-rate * t);
}
// ---------------------------------------------
// Extrapolation beyond the last date
if (!date.isBefore(timePoints[_numberOfInstruments - 1])) {
final int lo = _numberOfInstruments - 2;
final int hi = _numberOfInstruments - 1;
t1 = TimeCalculator.getTimeBetween(valueDate, timePoints[lo], ACT_360);
t2 = TimeCalculator.getTimeBetween(valueDate, timePoints[hi], ACT_360);
t = TimeCalculator.getTimeBetween(valueDate, date, ACT_360);
//loRate = _zCurveCCRates[lo];
//hiRate = _zCurveCCRates[hi];
loRate = ccRates[lo];
hiRate = ccRates[hi];
z1 = Math.log(1.0 + loRate);
z2 = Math.log(1.0 + hiRate);
// TODO : DoubleToBits this
if (t == 0.0) {
// TODO : Check for t2 == 0 as well
t = 1.0 / 365.0;
}
final double zt = getInterpolatedRate(t, t1, t2, z1, z2);
rate = zt / t;
t = TimeCalculator.getTimeBetween(valueDate, date, ACT_365);
Z = Math.exp(-rate * t);
}
// ---------------------------------------------
// Interpolation
if (!date.isBefore(timePoints[0]) && date.isBefore(timePoints[_numberOfInstruments - 1]))
{
// ... date is within the window spanned by the input dates
int lo = 0;
// Start at the first date
ZonedDateTime rollingDate = timePoints[0];
while (!rollingDate.isAfter(date)) {
lo++;
rollingDate = timePoints[lo];
}
final int hi = lo + 1;
loDate = timePoints[lo - 1];
hiDate = timePoints[hi - 1];
//loRate = _zCurveCCRates[lo - 1];
//hiRate = _zCurveCCRates[hi - 1];
loRate = ccRates[lo - 1];
hiRate = ccRates[hi - 1];
z1 = Math.log(1.0 + loRate);
z2 = Math.log(1.0 + hiRate);
t1 = TimeCalculator.getTimeBetween(valueDate, loDate, ACT_360);
t2 = TimeCalculator.getTimeBetween(valueDate, hiDate, ACT_360);
t = TimeCalculator.getTimeBetween(valueDate, date, ACT_360);
// TODO : DoubleToBits this
if (t == 0.0) {
// TODO : Check for t2 == 0 as well
t = 1.0 / 365.0;
}
final double zt = getInterpolatedRate(t, t1, t2, z1, z2);
rate = zt / t;
t = TimeCalculator.getTimeBetween(valueDate, date, ACT_365);
Z = Math.exp(-rate * t);
}
// ---------------------------------------------
final double discountFactor = Z;
return discountFactor;
}
// ----------------------------------------------------------------------------------------------------------------------------------------
}