Package com.opengamma.analytics.financial.credit.isdayieldcurve

Source Code of com.opengamma.analytics.financial.credit.isdayieldcurve.ISDAYieldCurve

/**
* 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;
  }

  // ----------------------------------------------------------------------------------------------------------------------------------------
}
TOP

Related Classes of com.opengamma.analytics.financial.credit.isdayieldcurve.ISDAYieldCurve

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.