/* *************************************************************************** ** Deletes a holiday list structure. *************************************************************************** */ void JpmcdsHolidayListDelete (THolidayList *hl) /* (I) Holiday list to delete */ { if (hl != NULL) { JpmcdsFreeDateList (hl->dateList); FREE(hl); } }
/* *************************************************************************** ** Creates a new holiday structure. ** ** The date list can be NULL, in which case the resulting date list in the ** holiday structure will be a date list with no dates, e.g. ** hl->dateList->fNumItems = 0; *************************************************************************** */ THolidayList* JpmcdsHolidayListNewGeneral (TDateList *dateList, /* (I) Date list to use */ long weekends /* (I) Weekends flag - use JPMCDS_WEEKEND_... */ ) { static char routine[] = "JpmcdsHolidayListNewGeneral"; int status = FAILURE; THolidayList *hl = NULL; TDateList *dl = NULL; /* get new date list */ if (dateList == NULL) { dl = JpmcdsNewEmptyDateList (0); } else { dl = JpmcdsCopyDateList (dateList); } if (dl == NULL) { goto done; } /* get new holiday list */ hl = NEW(THolidayList); if (hl == NULL) { goto done; } /* fill in holiday list */ hl->dateList = dl; hl->weekends = weekends; dl = NULL; /* Now owned by hl */ if (verifyHolidayList (hl) != SUCCESS) goto done; status = SUCCESS; done: JpmcdsFreeDateList (dl); if (status != SUCCESS) { JpmcdsHolidayListDelete (hl); hl = NULL; JpmcdsErrMsg ("%s: Failed.\n", routine); } return hl; }
/* *************************************************************************** ** Adds dates to a TDateList and frees the input date list. ** ** If the original date list and date list to be added are sorted, then ** the resulting date list will be sorted and duplicate dates will be ** removed. Sorting assumes ascending order ([0] < [1] etc). ** ** If either of the inputs are not sorted, then the resulting date list ** will not be sorted, and some duplicates may remain. ** ** For efficiency, we do not automatically try to sort the resulting ** date list for unsorted inputs. Sorting the date list each time appears ** to be a huge performance issue in some algorithms (where the input ** dates would all be sorted anyway). ** ** Note that if dl is NULL, then this will create a new date list from ** the given dates. ** ** Note that if numItems=0, this will copy the given date list. ** ** The input date list is FREE'd by this routine. Thus if you have an ** algorithm which involves building up a datelist gradually, you can ** do something like this: ** ** TDateList* dl = NULL; ** ... ** dl = JpmcdsDateListAddDatesFreeOld (dl, numItems, array); ** if (dl == NULL) goto done; ** .. ** dl = JpmcdsDateListAddDatesFreeOld (dl, numItems, array); ** if (dl == NULL) goto done; ** .. ** etc. ** ** with the point being that you don't have to worry about the original ** date list at each step since this routine frees it for you. *************************************************************************** */ TDateList* JpmcdsDateListAddDatesFreeOld (TDateList *dl, /* (I/O) Initial date list - gets freed */ int numItems, /* (I) Number of dates to add */ TDate *array) /* (I) [numItems] Dates to be added */ { static char routine[] = "JpmcdsDateListAddDatesFreeOld"; TDateList *output; output = JpmcdsDateListAddDates (dl, numItems, array); JpmcdsFreeDateList (dl); if (output == NULL) JpmcdsErrMsgFailure(routine); return output; }
/* *************************************************************************** ** Adds dates to a TDateList. ** ** If the original date list and date list to be added are sorted, then ** the resulting date list will be sorted and duplicate dates will be ** removed. Sorting assumes ascending order ([0] < [1] etc). ** ** If either of the inputs are not sorted, then the resulting date list ** will not be sorted, and some duplicates may remain. ** ** For efficiency, we do not automatically try to sort the resulting ** date list for unsorted inputs. Sorting the date list each time appears ** to be a huge performance issue in some algorithms (where the input ** dates would all be sorted anyway). ** ** Note that if dl is NULL, then this will create a new date list from ** the given dates. ** ** Note that if numItems=0, this will copy the given date list. *************************************************************************** */ TDateList* JpmcdsDateListAddDates (TDateList *dl, /* (I) Initial date list */ int numItems, /* (I) Number of dates to add */ TDate *array) /* (I) [numItems] Dates to be added */ { static char routine[] = "JpmcdsDateListAddDates"; int status = FAILURE; TDateList tmp = {0, NULL}; TDateList *result = NULL; REQUIRE (numItems >= 0); REQUIRE (dl == NULL || dl->fNumItems >= 0); if (dl == NULL) { result = JpmcdsNewDateListFromDates (array, numItems); } else if (numItems <= 0) { result = JpmcdsCopyDateList (dl); } else if (dl->fNumItems == 0 && numItems == 0) { result = JpmcdsNewDateListFromDates (NULL, 0); } else { int totalItems = dl->fNumItems + numItems; int i = 0; int j = 0; int k = 0; result = JpmcdsNewEmptyDateList (totalItems); if (result == NULL) goto done; while (i < dl->fNumItems && j < numItems) { if (dl->fArray[i] == array[j]) { /* exclude duplicates */ ++j; --totalItems; } else if (dl->fArray[i] < array[j]) { result->fArray[k] = dl->fArray[i]; ++i; ++k; } else { // assert (dl->fArray[i] > array[j]); result->fArray[k] = array[j]; ++j; ++k; } } if (i < dl->fNumItems) { int n = dl->fNumItems - i; COPY_ARRAY (result->fArray+k, dl->fArray+i, TDate, n); k += n; } if (j < numItems) { int n = numItems - j; COPY_ARRAY (result->fArray+k, array+j, TDate, n); k += n; } // assert (k == totalItems); result->fNumItems = totalItems; } if (result == NULL) goto done; status = SUCCESS; done: if (status != SUCCESS) { JpmcdsErrMsgFailure (routine); JpmcdsFreeDateList (result); result = NULL; } FREE(tmp.fArray); return result; }
/* *************************************************************************** ** Makes a date list from a given start date to a given end date with dates ** seperated by a given interval. ** ** Use the stub parameter to determine whether the stub appears at the ** start or the end of the date list, and whether the stub is long or ** short. ** ** The start date and end date are always both in the date list. ** The end date must be strictly after the start date. ** The date interval must be positive. *************************************************************************** */ TDateList* JpmcdsDateListMakeRegular (TDate startDate, /* (I) Start date */ TDate endDate, /* (I) End date */ TDateInterval *interval, /* (I) Date interval */ TStubMethod *stubType) /* (I) Stub type */ { static char routine[] = "JpmcdsDateListMakeRegular"; int status = FAILURE; TDateList *dl = NULL; TDate tmpDates[100]; int i; int numIntervals; int numTmpDates = sizeof(tmpDates) / sizeof(TDate); int totalDates = 0; TDate date; TDateInterval multiInterval; REQUIRE (interval != NULL); REQUIRE (interval->prd > 0); REQUIRE (endDate > startDate); /* we calculate tmpDates in blocks of 100 and add to the datelist */ if (!stubType->stubAtEnd) { /* front stub - so we start at endDate and work backwards */ numIntervals = 0; i = numTmpDates; date = endDate; while (date > startDate) { if (i == 0) { dl = JpmcdsDateListAddDatesFreeOld (dl, numTmpDates, tmpDates); if (dl == NULL) goto done; i = numTmpDates; } --i; --numIntervals; ++totalDates; // assert (i >= 0); tmpDates[i] = date; SET_TDATE_INTERVAL(multiInterval, interval->prd * numIntervals, interval->prd_typ); if (JpmcdsDtFwdAny(endDate, &multiInterval, &date) != SUCCESS) goto done; } // assert (totalDates > 0); // assert (date <= startDate); if (date == startDate || totalDates == 1 || !stubType->longStub) { /* don't change existing tmpDates[] but need to add startDate */ if (i == 0) { dl = JpmcdsDateListAddDatesFreeOld (dl, numTmpDates, tmpDates); if (dl == NULL) goto done; i = numTmpDates; } --i; ++totalDates; tmpDates[i] = startDate; } else { // assert (!stubType->stubAtEnd && stubType->longStub); // assert (date < startDate); /* the existing date in tmpDates[] should be changed to be the start date */ tmpDates[i] = startDate; } /* now add from tmpDates[i] to tmpDates[numTmpDates-1] to date list */ dl = JpmcdsDateListAddDatesFreeOld (dl, numTmpDates-i, tmpDates+i); if (dl == NULL) goto done; } else { /* back stub - so we start at startDate and work forwards */ numIntervals = 0; i = -1; date = startDate; while (date < endDate) { ++i; ++totalDates; if (i == numTmpDates) { dl = JpmcdsDateListAddDatesFreeOld (dl, numTmpDates, tmpDates); if (dl == NULL) goto done; i = 0; } ++numIntervals; // assert (i < numTmpDates); tmpDates[i] = date; SET_TDATE_INTERVAL(multiInterval, interval->prd * numIntervals, interval->prd_typ); if (JpmcdsDtFwdAny(startDate, &multiInterval, &date) != SUCCESS) goto done; } // assert (totalDates > 0); // assert (date >= endDate); if (date == endDate || totalDates == 1 || stubType->stubAtEnd && !stubType->longStub) { /* don't change existing tmpDates[] but need to add endDate */ ++i; ++totalDates; if (i == numTmpDates) { dl = JpmcdsDateListAddDatesFreeOld (dl, numTmpDates, tmpDates); if (dl == NULL) goto done; i = 0; } tmpDates[i] = endDate; } else { // assert (stubType->stubAtEnd && stubType->longStub); // assert (date > endDate); /* the existing date in tmpDates[] should be changed to be the end date */ tmpDates[i] = endDate; } /* now add from tmpDates[0] to tmpDates[i] to the date list */ dl = JpmcdsDateListAddDatesFreeOld (dl, i+1, tmpDates); if (dl == NULL) goto done; } // ASSERT (totalDates >= 2); // ASSERT (dl->fNumItems == totalDates); status = SUCCESS; done: if (status != SUCCESS) { JpmcdsFreeDateList (dl); dl = NULL; JpmcdsErrMsgFailure (routine); } return dl; }
/* *************************************************************************** ** Calculates the PV of a fee leg with fixed fee payments. *************************************************************************** */ int JpmcdsFeeLegPV (TFeeLeg *fl, TDate today, TDate stepinDate, TDate valueDate, TCurve *discCurve, TCurve *spreadCurve, TBoolean payAccruedAtStart, double *pv) { static char routine[] = "JpmcdsFeeLegPV"; int status = FAILURE; int i; double myPv; double valueDatePv; TDateList *tl = NULL; TDate matDate; REQUIRE (spreadCurve != NULL); REQUIRE (fl != NULL); REQUIRE (discCurve != NULL); REQUIRE (spreadCurve != NULL); REQUIRE (pv != NULL); REQUIRE (valueDate >= today); REQUIRE (stepinDate >= today); myPv = 0.0; if (fl->nbDates > 1) { /* it is more efficient to compute the timeLine just the once and truncate it for each payment */ TDate startDate = fl->accStartDates[0]; TDate endDate = fl->accEndDates[fl->nbDates-1]; tl = JpmcdsRiskyTimeLine(startDate, endDate, discCurve, spreadCurve); if (tl == NULL) goto done; } matDate = (fl->obsStartOfDay == TRUE ? fl->accEndDates[fl->nbDates - 1] - 1 : fl->accEndDates[fl->nbDates - 1]); if(today > matDate || stepinDate > matDate) { status = SUCCESS; *pv = 0; goto done; } for (i = 0; i < fl->nbDates; ++i) { double thisPv = 0; if (FeePaymentPVWithTimeLine (fl->accrualPayConv, today, stepinDate, fl->accStartDates[i], fl->accEndDates[i], fl->payDates[i], fl->dcc, fl->notional, fl->couponRate, discCurve, spreadCurve, tl, fl->obsStartOfDay, &thisPv) != SUCCESS) goto done; myPv += thisPv; } valueDatePv = JpmcdsForwardZeroPrice (discCurve, today, valueDate); *pv = myPv / valueDatePv; if(payAccruedAtStart) /* clean price */ { double ai; if(FeeLegAI(fl, stepinDate, &ai) == FAILURE) { JpmcdsErrMsg ("%s: accrued interest calculation failed.\n", routine); goto done; } *pv -= ai; } status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsgFailure (routine); JpmcdsFreeDateList (tl); return status; }
/* *************************************************************************** ** Calculates the PV of the accruals which occur on default. ** Uses a pre-calculated timeline for efficiency. *************************************************************************** */ int JpmcdsAccrualOnDefaultPVWithTimeLine (TDate today, TDate stepinDate, TDate startDate, TDate endDate, double amount, TCurve *discCurve, TCurve *spreadCurve, TDateList *criticalDates, double *pv) { static char routine[] = "JpmcdsAccrualOnDefaultPVWithTimeLine"; int status = FAILURE; double myPv = 0.0; int i; double t; double s0; double s1; double df0; double df1; double accRate; TDate subStartDate; TDateList *tl = NULL; REQUIRE (endDate > startDate); REQUIRE (discCurve != NULL); REQUIRE (spreadCurve != NULL); REQUIRE (pv != NULL); /* ** Timeline is points on the spreadCurve between startDate and endDate, ** combined with points from the discCurve, plus ** the startDate and endDate. */ if (criticalDates != NULL) { tl = JpmcdsTruncateTimeLine (criticalDates, startDate, endDate); } else { tl = JpmcdsRiskyTimeLine (startDate, endDate, discCurve, spreadCurve); } if (tl == NULL) goto done; /* the integration - we can assume flat forwards between points on the timeline - this is true for both curves we are integrating -Zt dS/dt where Z is the discount factor and S is the survival probability and t is the accrual time assuming flat forwards on each part of the integration, this is an exact integral */ subStartDate = MAX(stepinDate, startDate); t = (double)(endDate-startDate)/365.0; accRate = amount/t; s0 = JpmcdsForwardZeroPrice(spreadCurve, today, subStartDate); df0 = JpmcdsForwardZeroPrice(discCurve, today, MAX(today, subStartDate)); for (i = 1; i < tl->fNumItems; ++i) { double lambda; double fwdRate; double thisPv; double t0; double t1; double lambdafwdRate; if(tl->fArray[i] <= stepinDate) continue; s1 = JpmcdsForwardZeroPrice(spreadCurve, today, tl->fArray[i]); df1 = JpmcdsForwardZeroPrice(discCurve, today, tl->fArray[i]); t0 = (double)(subStartDate + 0.5 - startDate)/365.0; t1 = (double)(tl->fArray[i] + 0.5- startDate)/365.0; t = t1-t0; lambda = log(s0/s1)/t; fwdRate = log(df0/df1)/t; lambdafwdRate = lambda + fwdRate + 1.0e-50; thisPv = lambda * accRate * s0 * df0 * ( (t0 + 1.0/(lambdafwdRate))/(lambdafwdRate) - (t1 + 1.0/(lambdafwdRate))/(lambdafwdRate) * s1/s0 * df1/df0); myPv += thisPv; s0 = s1; df0 = df1; subStartDate = tl->fArray[i]; } status = SUCCESS; *pv = myPv; done: if (status != SUCCESS) JpmcdsErrMsgFailure (routine); JpmcdsFreeDateList (tl); return status; }
/* *************************************************************************** ** Reads a holiday file into memory as a holiday list. ** ** Structure of holiday file, ascii text file of lines like: ** # - commment (blank lines are also skipped) ** 19631026 - means 10/26/1963 is a holiday ** # SATURDAY_NOT_ALWAYS_HOLIDAY - sets "saturday isn't always a holiday" ** # SUNDAY_NOT_ALWAYS_HOLIDAY - sets "sunday isn't always a holiday" ** # MONDAY_ALWAYS_HOLIDAY - sets "monday as always a holiday" ** # TUESDAY_ALWAYS_HOLIDAY - sets "tuesday as always a holiday" ** # WEDNESDAY_ALWAYS_HOLIDAY - sets "wednesday as always a holiday" ** # THURDSAY_ALWAYS_HOLIDAY - sets "thursday as always a holiday" ** # FRIDAY_ALWAYS_HOLIDAY - sets "friday as always a holiday" ** ** Dates must be in increasing order. *************************************************************************** */ THolidayList* JpmcdsHolidayListRead (char *fileName /* (I) Name of file to read (may differ) */ ) { static char routine[] = "JpmcdsHolidayListRead"; int status = FAILURE; int numHols; /* count of holidays */ int idx; /* counts over holidays */ char buffer[MAX_BUFFER]; /* line of text from file */ char ucBuffer[MAX_BUFFER];/* upper case line of text from file */ TFile *fp = NULL; /* file handle */ THolidayList *hl = NULL; /* to be returned */ TDateList *dl = NULL; /* date list read in */ long weekends; fp = JpmcdsFopen (fileName, JPMCDS_FREAD); if (fp == NULL) { JpmcdsErrMsg ("%s: Couldn't open file %s.\n", routine, fileName); goto done; } /* count number of holidays specified */ numHols = 0; while (JpmcdsFgets (buffer, MAX_BUFFER, fp) == SUCCESS) { if (atol (buffer) > 16010101) numHols++; } if (JpmcdsFclose (fp) == FAILURE) /* close file, reopen to read again */ { fp = NULL; goto done; } fp = NULL; fp = JpmcdsFopen (fileName, JPMCDS_FREAD); if (fp == NULL) { JpmcdsErrMsg ("%s: Couldn't open file %s twice.\n", routine, fileName); goto done; } dl = JpmcdsNewEmptyDateList (numHols); if (dl == NULL) goto done; idx = 0; weekends = JPMCDS_WEEKEND_STANDARD; /* Includes SAT and SUN */ while (JpmcdsFgets (buffer, MAX_BUFFER, fp) == SUCCESS) { if (buffer[0] == '#') { static char monString[] = "# MONDAY_ALWAYS_HOLIDAY"; static char tueString[] = "# TUESDAY_ALWAYS_HOLIDAY"; static char wedString[] = "# WEDNESDAY_ALWAYS_HOLIDAY"; static char thuString[] = "# THURSDAY_ALWAYS_HOLIDAY"; static char friString[] = "# FRIDAY_ALWAYS_HOLIDAY"; static char satString[] = "# SATURDAY_NOT_ALWAYS_HOLIDAY"; static char sunString[] = "# SUNDAY_NOT_ALWAYS_HOLIDAY"; COPY_CAPITALIZE(ucBuffer, buffer, MAX_BUFFER-1); if (strncmp (buffer, monString, sizeof(monString)-1) == 0) weekends |= JPMCDS_WEEKEND_MONDAY; if (strncmp (buffer, tueString, sizeof(tueString)-1) == 0) weekends |= JPMCDS_WEEKEND_TUESDAY; if (strncmp (buffer, wedString, sizeof(wedString)-1) == 0) weekends |= JPMCDS_WEEKEND_WEDNESDAY; if (strncmp (buffer, thuString, sizeof(thuString)-1) == 0) weekends |= JPMCDS_WEEKEND_THURSDAY; if (strncmp (buffer, friString, sizeof(friString)-1) == 0) weekends |= JPMCDS_WEEKEND_FRIDAY; if (strncmp (buffer, satString, sizeof(satString)-1) == 0) weekends &= ~JPMCDS_WEEKEND_SATURDAY; if (strncmp (buffer, sunString, sizeof(sunString)-1) == 0) weekends &= ~JPMCDS_WEEKEND_SUNDAY; } else { long dateVal = atol (buffer); if (dateVal > 16010101) { TMonthDayYear mdy; TDate thisDate; if (idx >= numHols) { JpmcdsErrMsg ("%s: More dates on second scan of file %s.\n", routine, fileName); goto done; } mdy.year = dateVal / 10000; mdy.month = (dateVal - mdy.year * 10000) / 100; mdy.day = dateVal % 100; if (JpmcdsMDYToDate (&mdy, &thisDate) == FAILURE) { JpmcdsErrMsg ("%s: invalid date: %s", routine, buffer); goto done; } if (idx > 0 && dl->fArray[idx-1] >= thisDate) { JpmcdsErrMsg ("%s: Date out of order: %s", routine, buffer); goto done; } dl->fArray[idx] = thisDate; ++idx; } } } if (numHols < 1 && weekends == JPMCDS_WEEKEND_STANDARD) { JpmcdsErrMsg ("%s: No holiday information found in %s.\n", routine, fileName); JpmcdsErrMsg (" Either week-end information or dates must be provided.\n"); goto done; } if (idx != numHols) { JpmcdsErrMsg ("%s: Less dates on second scan of file %s\n", routine, fileName); goto done; } if (JpmcdsFclose (fp) == FAILURE) { fp = NULL; goto done; } fp = NULL; hl = JpmcdsHolidayListNewGeneral (dl, weekends); if (hl == NULL) goto done; status = SUCCESS; done: JpmcdsFclose (fp); JpmcdsFreeDateList (dl); if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return hl; }
/* *************************************************************************** ** Computes a one period integral *************************************************************************** */ static int onePeriodIntegral (TDate today, TDate startDate, TDate endDate, TCurve *discCurve, TCurve *spreadCurve, double recoveryRate, double *pv) { static char routine[] = "onePeriodIntegral"; int status = FAILURE; double myPv = 0.0; int i; double t; double s0; double s1; double df0; double df1; double loss; TDateList *tl = NULL; REQUIRE (endDate > startDate); REQUIRE (discCurve != NULL); REQUIRE (spreadCurve != NULL); REQUIRE (pv != NULL); if (today > endDate) { myPv = 0.0; goto success; } tl = JpmcdsRiskyTimeLine (startDate, endDate, discCurve, spreadCurve); if (tl == NULL) goto done; /* the integration - we can assume flat forwards between points on the timeline - this is true for both curves we are integrating -Z dS/dt where Z is the discount factor and S is the survival probability assuming flat forwards on each part of the integration, this is an exact integral */ s1 = JpmcdsForwardZeroPrice(spreadCurve, today, startDate); df1 = JpmcdsForwardZeroPrice(discCurve, today, MAX(today, startDate)); loss = 1.0 - recoveryRate; for (i = 1; i < tl->fNumItems; ++i) { double lambda; double fwdRate; double thisPv; s0 = s1; df0 = df1; s1 = JpmcdsForwardZeroPrice(spreadCurve, today, tl->fArray[i]); df1 = JpmcdsForwardZeroPrice(discCurve, today, tl->fArray[i]); t = (double)(tl->fArray[i] - tl->fArray[i-1])/365.0; lambda = log(s0/s1)/t; fwdRate = log(df0/df1)/t; thisPv = loss * lambda / (lambda + fwdRate) * (1.0 - exp(-(lambda + fwdRate) * t)) * s0 * df0; myPv += thisPv; } success: status = SUCCESS; *pv = myPv; done: if (status != SUCCESS) JpmcdsErrMsgFailure (routine); JpmcdsFreeDateList (tl); return status; }
/* *************************************************************************** ** Makes a fixed fee leg for a vanilla CDS. ** Note that you are protected on both startDate and endDate. *************************************************************************** */ TFeeLeg* JpmcdsCdsFeeLegMake (TDate startDate, TDate endDate, TBoolean payAccOnDefault, TDateInterval *dateInterval, TStubMethod *stubType, double notional, double couponRate, long paymentDcc, long badDayConv, char *calendar, TBoolean protectStart) { static char routine[] = "JpmcdsCdsFeeLegMake"; int status = FAILURE; TDateList *dl = NULL; TFeeLeg *fl = NULL; TDateInterval ivl3M; TDate prevDate; TDate prevDateAdj; int i; SET_TDATE_INTERVAL(ivl3M,3,'M'); if (dateInterval == NULL) dateInterval = &ivl3M; if(protectStart) REQUIRE (endDate >= startDate); else REQUIRE (endDate > startDate); if(protectStart && endDate == startDate) { TDate dates[2]; dates[0] = startDate; dates[1] = endDate; dl = JpmcdsNewDateListFromDates(dates, 2); } else { dl = JpmcdsDateListMakeRegular (startDate, endDate, dateInterval, stubType); } if (dl == NULL) goto done; /* the datelist includes both start date and end date */ /* therefore it has one more element than the fee leg requires */ fl = JpmcdsFeeLegMakeEmpty (dl->fNumItems-1); if (fl == NULL) goto done; if (payAccOnDefault) { fl->accrualPayConv = ACCRUAL_PAY_ALL; } else { fl->accrualPayConv = ACCRUAL_PAY_NONE; /* and we will assume that it observes at end of the period */ } fl->dcc = paymentDcc; prevDate = dl->fArray[0]; prevDateAdj = prevDate; /* first date is not bad day adjusted */ for (i = 0; i < fl->nbDates; ++i) { TDate nextDate = dl->fArray[i+1]; TDate nextDateAdj; if (JpmcdsBusinessDay (nextDate, badDayConv, calendar, &nextDateAdj) != SUCCESS) goto done; fl->accStartDates[i] = prevDateAdj; fl->accEndDates[i] = nextDateAdj; fl->payDates[i] = nextDateAdj; prevDate = nextDate; prevDateAdj = nextDateAdj; } fl->notional = notional; fl->couponRate = couponRate; /* the last accrual date is not adjusted */ /* also we may have one extra day of accrued interest */ if (protectStart) { fl->accEndDates[fl->nbDates-1] = prevDate+1; fl->obsStartOfDay = TRUE; } else { fl->accEndDates[fl->nbDates-1] = prevDate; fl->obsStartOfDay = FALSE; } status = SUCCESS; done: if (status != SUCCESS) { JpmcdsErrMsgFailure(routine); JpmcdsFeeLegFree (fl); fl = NULL; } JpmcdsFreeDateList (dl); return fl; }
/* *************************************************************************** ** Makes a cashflow list from a DateList. Cashflows (for non stub payments) ** are equal to the rate times the fraction of the year between each date ** and the previous date. The year fraction is calculated by the year ** fraction function passed in. For stub payments, Actual/Actual is used. ** ** If there is a stub at the BEGINNING, then the first element of the ** dateList is assumed to PRECEED startDate. ** ** If there is a stub at the END, then the last element of the dateList is ** assumed to COME AFTER maturityDate. *************************************************************************** */ TCashFlowList* JpmcdsNewCFLFromDL3 (TDateList *dates, /* (I) Dates */ TDate startDate, /* (I) startDate */ TDate maturityDate, /* (I) MaturityDate */ double rate, /* (I) Multiplied by yearFrac */ long couponDayCountConv, /* (I) Coupon calc */ TBoolean longStub, /* (I) Long stub */ long accrualBadDayConv, /* (I) Accrual bad day convention */ long payBadDayConv, /* (I) Pay bad day convention */ char *accrualHolidayFile, /* (I) Accrual Holiday file. */ char *payHolidayFile, /* (I) Payment Holiday file. */ TBoolean usePreStartDate) /* (I) Use prestart date if stub */ { static char routine[]="JpmcdsNewCFLFromDL3"; int idx; int startIdx; /* First index for regular proc */ int endIdx; /* Last index for regular proc */ int numDates = dates->fNumItems; TDate accrualStart, payStart; /* Accrue and pay start dates */ TDate accrualMaturity, payMaturity; /* Accrue and pay maturity dates */ TDateList *accrualDates = NULL; TDateList *payDates = NULL; TBoolean frontStub, backStub; TDateList *datesCopy = NULL; TCashFlowList *cfl = NULL; int status = FAILURE; if ( numDates == 0 ) { cfl = JpmcdsNewEmptyCFL(0); if ( cfl == NULL ) goto done; status = SUCCESS; goto done; } frontStub = ( startDate > dates->fArray[0] ); backStub = ( maturityDate < dates->fArray[numDates-1] ); /* For long stubs, simply coalesce first/last two coupon periods * (i.e. short stub + adjacent regular period) into one... */ if ( longStub && (frontStub || backStub) && numDates > 2 ) { datesCopy = JpmcdsNewEmptyDateList(numDates - 1); if ( datesCopy == NULL ) goto done; if ( frontStub ) { datesCopy->fArray[0] = dates->fArray[0]; memcpy(&datesCopy->fArray[1], &dates->fArray[2], (numDates - 2) * sizeof(TDate)); dates = datesCopy; /* simply overwrite pointer, hiding original */ --numDates; } if (backStub) { memcpy(&datesCopy->fArray[0], &dates->fArray[0], (numDates - 2) * sizeof(TDate)); datesCopy->fArray[numDates - 2] = dates->fArray[numDates - 1]; dates = datesCopy; /* simply overwrite pointer, hiding original */ --numDates; } } cfl = JpmcdsNewEmptyCFL(numDates); if( cfl == NULL ) goto done; /* Get accrualDates */ if (accrualBadDayConv == JPMCDS_BAD_DAY_NONE) { accrualDates = dates; /* efficiency */ accrualStart = startDate; accrualMaturity = maturityDate; } else { /* Adjust for bad days */ accrualDates = JpmcdsNewDateListBusDayAdj(dates, accrualBadDayConv, accrualHolidayFile); if (accrualDates == NULL) goto done; if (JpmcdsBusinessDay(startDate, accrualBadDayConv, accrualHolidayFile, &accrualStart) == FAILURE) goto done; if (JpmcdsBusinessDay(maturityDate, accrualBadDayConv, accrualHolidayFile, &accrualMaturity) == FAILURE) goto done; } /* Get payDates */ if (payBadDayConv == JPMCDS_BAD_DAY_NONE) { payDates = dates; /* efficiency */ payStart = startDate; payMaturity = maturityDate; } else { /* Adjust for bad days */ payDates = JpmcdsNewDateListBusDayAdj(dates, payBadDayConv, payHolidayFile); if (payDates == NULL) goto done; if (JpmcdsBusinessDay(startDate, payBadDayConv, payHolidayFile, &payStart) == FAILURE) goto done; if (JpmcdsBusinessDay(maturityDate, payBadDayConv, payHolidayFile, &payMaturity) == FAILURE) goto done; } if (usePreStartDate) /* Useful to JpmcdsTrinomialModel */ cfl->fArray[0].fDate = payDates->fArray[0]; else cfl->fArray[0].fDate = payStart; cfl->fArray[0].fAmount = 0.; cfl->fArray[numDates-1].fDate = payMaturity; for (idx=1; idx < numDates-1; idx++) cfl->fArray[idx].fDate = payDates->fArray[idx]; startIdx = 1; endIdx = numDates-1; if ( frontStub ) { startIdx = 2; /* For non-stub processing */ if (JpmcdsStubPayment(accrualDates->fArray[0], /*(I)Coupon start*/ accrualDates->fArray[1], /*(I)Coupon end */ accrualStart, /*(I)Accrue start*/ accrualDates->fArray[1], /*(I)Accrue End */ rate, /*(I) */ couponDayCountConv, /*(I) */ &cfl->fArray[1].fAmount) /*(O) */ == FAILURE) goto done; } if ( backStub ) { endIdx = numDates-2; /* For non-stub processing */ if (JpmcdsStubPayment(accrualDates->fArray[numDates-2], /*(I)Coup start*/ accrualDates->fArray[numDates-1], /*(I)Coupon end*/ accrualDates->fArray[numDates-2], /*(I)Accru start*/ accrualMaturity, /*(I)Accrue end*/ rate, /*(I)*/ couponDayCountConv, /*(I)*/ &cfl->fArray[numDates-1].fAmount) /*(O)*/ == FAILURE) goto done; } /* Compute non-stub cashflows */ for (idx=startIdx; idx <= endIdx; idx++) { double yearFrac; if (JpmcdsDayCountFraction(accrualDates->fArray[idx-1], accrualDates->fArray[idx], couponDayCountConv, &yearFrac) == FAILURE) goto done; cfl->fArray[idx].fAmount = rate * yearFrac; } status = SUCCESS; done: JpmcdsFreeDateList(datesCopy); if (payDates != dates) JpmcdsFreeDateList(payDates); if (accrualDates != dates) JpmcdsFreeDateList(accrualDates); if (status == FAILURE) { JpmcdsFreeCFL(cfl); return NULL; } return cfl; }
/* *************************************************************************** ** Makes a new cash flow list. ** ** Depending on the value of flags, an initial paymemt of value 1 is ** subtracted and/or a final payment of 1 is added. If ** JPMCDS_PRESTART_ZERO_PAYMENT is set, and there is a front stub, the first ** date will be the payment date that would have occured before the start ** date. In this case, JPMCDS_SUBTRACT_INITIAL cannot be set. *************************************************************************** */ TCashFlowList* JpmcdsMakeCFL (double couponRate, /* (I) Annual coupon Rate */ TDate startDate, /* (I) Date instrument begins at */ TDateInterval *interval, /* (I) Time between payments */ TDate maturityDate, /* (I) Date instrument matures at*/ long couponDayCountConv, /* (I) For coupon calculation */ long stubPlacement, /* (I) [Front / Back] + [Short (default) / Long] */ long flags, /* (I) What type (See cashflow.h) */ long accrualBadDayConv, /* (I) Accrual bad day convention */ long payBadDayConv, /* (I) Pay bad day convention */ char *holidayFile) /* (I) Holiday file */ { static char routine[]="JpmcdsMakeCFLRoll"; TDateList *dl = NULL; TCashFlowList *cfl = NULL; if (maturityDate <= startDate) { JpmcdsErrMsg("%s: maturityDate(%s) <= startDate(%s)\n", routine, JpmcdsFormatDate(maturityDate), JpmcdsFormatDate(startDate)); return NULL; } if ((flags & JPMCDS_SUBTRACT_INITIAL || flags & JPMCDS_KEEP_START_DATE) && flags & JPMCDS_PRESTART_ZERO_PAYMENT) { JpmcdsErrMsg("%s: cannot subtract initial payment at prestart date.\n", routine); JpmcdsErrMsg("\tAlso cannot keep both the start and prestart dates.\n"); return NULL; } if (flags & JPMCDS_POST_MATURITY_ZERO_PAYMENT) { if (stubPlacement & JPMCDS_STUB_POSN_MASK) { JpmcdsErrMsg("%s: no post maturity zero payment w/ stub at end.\n", routine); return NULL; } else if (flags & JPMCDS_ADD_FINAL) { JpmcdsErrMsg("%s: no post maturity zero payment w/ final payment.\n", routine); return NULL; } else /* Advance maturityDate by interval */ { if (JpmcdsDtFwdAny(maturityDate, interval, &maturityDate) == FAILURE) goto done; } } /* Set up the TDateList of coupon dates; if stub at end, last date * could be later than maturityDate. If there is a stub at the start, 1st date * could be earlier than startDate. */ dl = JpmcdsNewDateListExtendedRoll(startDate, maturityDate, startDate /* roll date */, interval, stubPlacement & JPMCDS_STUB_POSN_MASK); if (dl == (TDateList *)NULL) goto done; /* Make cash flow list based on an annual rate of couponRate. * The startDate is included, with a cashflow of 0. */ cfl = JpmcdsNewCFLFromDL3(dl, startDate, maturityDate, couponRate, couponDayCountConv, stubPlacement & JPMCDS_STUB_LENGTH_LONG, accrualBadDayConv, payBadDayConv, holidayFile, holidayFile, (TBoolean)(flags & JPMCDS_PRESTART_ZERO_PAYMENT)); JpmcdsFreeDateList(dl); /* Free memory */ if (cfl == NULL) goto done; if( cfl->fNumItems == 0 ) return cfl; /* Initial payment of principal */ if (flags & JPMCDS_SUBTRACT_INITIAL) { cfl->fArray[0].fAmount-= 1.; } /* Add on the final repayment of principal */ if (flags & JPMCDS_ADD_FINAL) { cfl->fArray[cfl->fNumItems-1].fAmount += 1.; } if (flags & JPMCDS_POST_MATURITY_ZERO_PAYMENT) { cfl->fArray[cfl->fNumItems-1].fAmount = 0.; } /* Dont want zero-cashflows around, so move * all cashflows back by one, unless JPMCDS_PRESTART_ZERO_PAYMENT * or JPMCDS_KEEP_START_DATE is set. Testing equality to zero * is no problem since no calculations were done. */ if (!(flags & JPMCDS_PRESTART_ZERO_PAYMENT || flags & JPMCDS_KEEP_START_DATE) && cfl->fArray[0].fAmount == 0.) { int idx; for (idx=0; idx < cfl->fNumItems-1; idx++) { cfl->fArray[idx] = cfl->fArray[idx+1]; } cfl->fNumItems--; } done: if (cfl == NULL) JpmcdsErrMsg("%s: Failed.\n", routine); return cfl; }