/* *************************************************************************** ** Equivalent to JpmcdsIsBusinessDay but uses THolidayList structure in place ** of holiday file name. ** ** Indicates whether a date is a business day. *************************************************************************** */ int JpmcdsHolidayListIsBusinessDay (TDate date, /* (I) Input Date */ THolidayList *hl, /* (I) Holiday list structure */ TBoolean *isBusinessDay) /* (O) TRUE if a business day */ { static char routine[] = "JpmcdsHolidayListIsBusinessDay"; TBoolean aHoliday = FALSE; /* Must have a holiday list */ if (hl == NULL) { JpmcdsErrMsg ("%s: hl is NULL.\n", routine); return FAILURE; } /* First check for week-ends */ if (JPMCDS_IS_WEEKEND (date, hl->weekends)) { *isBusinessDay = FALSE; return SUCCESS; } /* Check if it is a holiday. */ if (JpmcdsHolidayListIsHoliday (date, hl, &aHoliday) == FAILURE) { JpmcdsErrMsg("%s: Failed.\n", routine); return FAILURE; } *isBusinessDay = !aHoliday; return SUCCESS; }
/* *************************************************************************** ** Read array of doubles from Excel. *************************************************************************** */ int ReadDoubleArray(TBoolean mandatory, OPER *oper, size_t n, double **values, char *routine, char *param) { size_t i; if (mandatory && oper->type == xltypeMissing) { JpmcdsErrMsg("%s: %s parameter is required\n", routine, param); return FAILURE; } if (oper->type == xltypeMulti) { if (oper->val.array.rows != n) { JpmcdsErrMsg("%s: %s - incorrect number of elements, expected %d\n", routine, param, n); return FAILURE; } *values = NEW_ARRAY(double, n); if (*values == NULL) return FAILURE; for (i = 0 ; i < n ; i++) { if (JpmcdsExcelGetDouble(oper->val.array.lparray + i, *values + i) != SUCCESS) return FAILURE; } } else { if (n != 1)
/* *************************************************************************** ** Validates a bad day convention. *************************************************************************** */ int JpmcdsBadDayConvValid (char *routine, long badDayConv) { switch (badDayConv) { case JPMCDS_BAD_DAY_FOLLOW: case JPMCDS_BAD_DAY_PREVIOUS: case JPMCDS_BAD_DAY_NONE: case JPMCDS_BAD_DAY_MODIFIED: return SUCCESS; default: /* ** There's a good chance that the user entered a lower-case ** version of a valid bad day convention. */ switch (toupper((int)badDayConv)) { case JPMCDS_BAD_DAY_FOLLOW: case JPMCDS_BAD_DAY_PREVIOUS: case JPMCDS_BAD_DAY_NONE: case JPMCDS_BAD_DAY_MODIFIED: JpmcdsErrMsg ("%s: Bad day convention %ld must be uppercase\n", routine, badDayConv); break; default: JpmcdsErrMsg ("%s: unknown bad day convention %ld\n", routine, badDayConv); break; } return FAILURE; } }
/* *************************************************************************** ** Checks whether a rate is valid between two dates. *************************************************************************** */ int JpmcdsRateValidYearFrac( char *routine, /* (I) Routine name to print */ double rate, /* (I) Rate to validate */ double yearFraction, /* (I) Fraction of year */ double basis) /* (I) Compounding basis */ { int status = FAILURE; switch ((long) basis) { case JPMCDS_SIMPLE_BASIS: if (rate * yearFraction <= -1.0) { JpmcdsErrMsg("%s: Simple Rate (%f) * Year Fraction (%f) must " "be > -1.0.\n", routine, rate, yearFraction); goto done; } break; case JPMCDS_DISCOUNT_RATE: if (rate * yearFraction >= 1.0) { JpmcdsErrMsg("%s: Discount Rate (%f) * Year Fraction (%f) must " "be < 1.0.\n", routine, rate, yearFraction); goto done; } break; case JPMCDS_CONTINUOUS_BASIS: /* Any rate is valid */ status = SUCCESS; goto done; case JPMCDS_DISCOUNT_FACTOR: if (rate <= 0.0) { JpmcdsErrMsg("%s: Discount factor (%f) must be > 0.0.\n", routine, rate); goto done; } break; default: if (rate <= -basis) { JpmcdsErrMsg("%s: Rate (%f) must be greater than -basis (%f).\n", routine, rate, -basis); goto done; } break; } status = SUCCESS; done: return status; }
/* *************************************************************************** ** Convert from a given rate to a discount factor. *************************************************************************** */ int JpmcdsRateToDiscount (double rate, /* (I) Rate */ TDate startDate, /* (I) Start date */ TDate endDate, /* (I) End date */ long rateDayCountConv, /* (I) Day count convention for rate */ long rateBasis, /* (I) Basis for the rate */ double *discount) /* (O) Discount factor */ { int status = FAILURE; /* Until proven successful */ static char routine[] = "JpmcdsRateToDiscount" ; double rateYF ; if (rateBasis == JPMCDS_DISCOUNT_FACTOR) { if (rate <= 0.0) { JpmcdsErrMsg("%s: Bad rate (discount factor) %f\n",routine,rate); goto done; } *discount = rate; return SUCCESS; } if (rateBasis < JPMCDS_SIMPLE_BASIS) { JpmcdsErrMsg ("%s: Basis (%ld) < %d.\n",routine,rateBasis, JPMCDS_SIMPLE_BASIS); goto done; /* Failed */ } /* get year fractions for rate */ if (JpmcdsDayCountFraction(startDate, endDate, rateDayCountConv, &rateYF) == FAILURE) { goto done; /* Failed */ } if (JpmcdsRateToDiscountYearFrac(rate, rateYF, (double)rateBasis, discount) != SUCCESS) { goto done; } status = SUCCESS; done: if (status == FAILURE) { JpmcdsErrMsg("%s: Failed.\n", routine); } return status; }
/* *************************************************************************** ** Adds a holiday list to the holiday cache. If the entry already exists ** in the cache, then the old version will be deleted. *************************************************************************** */ int JpmcdsHolidayListAddToCache (char *name, /* (I) Name to associate holidays with */ THolidayList *hl /* (I) Adds shallow copy */ ) { static char routine[] = "JpmcdsHolidayListAddToCache"; int status = FAILURE; THoliday *hol; THoliday *oldHol; /* JpmcdsNewHoliday creates a holiday structure with the holiday list as a shallow copy, but a deep copy of the capitalized name. This shallow copy is for efficiency. We do this call first so that we can be sure that either hl is owned by hol or hl is deleted. */ hol = JpmcdsNewHoliday(hl, name); if (hol == NULL) goto done; /* If necessary get rid of the old entry with the same name. Use hol->name since this has been capitalized. */ oldHol = holidayFind(hol->name); if (oldHol != NULL) { /* ** Cannot delete NONE or NO_WEEKENDS at this point. This is a sign ** that somebody is trying to overwrite these standard names. */ if (hol->name[0] == 'N') { if (strcmp(hol->name, "NONE") == 0 || strcmp (hol->name, "NO_WEEKENDS") == 0) { JpmcdsErrMsg ("%s: Attempt to over-write standard holiday %s\n", routine, hol->name); goto done; } } holidayDelete(oldHol); } /* Insert into cache */ hol->next = cache; cache = hol; hol = NULL; /* Now owned by cache */ status = SUCCESS; done: JpmcdsFreeHoliday (hol); if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** This function verifies a holiday list. This means the following actions ** are performed: ** 1. Validates that dates are in strictly increasing order. ** 2. Removes dates which are already defined by being a week-end. ** ** Currently does not perform stage 2 since this causes the buscache test ** driver to fail. We will consider re-introducing this feature after the ** release of version 9.4. *************************************************************************** */ static int verifyHolidayList (THolidayList *hl /* (I/O) Holiday list */ ) { #undef CHECK_FOR_WEEKENDS static char routine[] = "verifyHolidayList"; int status = FAILURE; long idx; long count; TDate lastDate = 0; count = 0; for (idx = 0; idx < hl->dateList->fNumItems; ++idx) { TDate thisDate = hl->dateList->fArray[idx]; if (thisDate <= lastDate) { JpmcdsErrMsg ("%s: Dates are not in strictly increasing order.\n", routine); JpmcdsErrMsg (" [%ld] = %s; LastDate = %s.\n", idx, JpmcdsFormatDate(thisDate), JpmcdsFormatDate(lastDate)); goto done; } lastDate = thisDate; /* remove weekends */ if (JPMCDS_IS_WEEKDAY(thisDate, hl->weekends)) { hl->dateList->fArray[count] = thisDate; ++count; } } hl->dateList->fNumItems = count; status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Indicates whether a date is a business day. *************************************************************************** */ int JpmcdsIsBusinessDay (TDate date, /* (I) Input Date */ char *name, /* (I) Filename w/ Holidays */ TBoolean *isBusinessDay) /* (O) */ { static char routine[] = "JpmcdsIsBusinessDay"; THolidayList *hl = NULL; int status = FAILURE; hl = JpmcdsHolidayListFromCache (name); if (hl == NULL) goto done; /* call the real routine, and return status */ if (JpmcdsHolidayListIsBusinessDay (date, hl, isBusinessDay) != SUCCESS) goto done; status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Calculates a business date being at offset business days ** from the original date *************************************************************************** */ int JpmcdsDateFromBusDaysOffset (TDate fromDate, /* (I) input date */ long offset, /* (I) number of business days */ char *holidayFile, /* (I) holiday file specification */ TDate *result) /* (O) resulting business date */ { static char routine[]="JpmcdsDateFromBusDaysOffset"; int status = FAILURE; THolidayList * hl = NULL; /* ** Search for the holiday file in the cache. */ hl = JpmcdsHolidayListFromCache (holidayFile); if (hl == NULL) goto done; /* call the real routine, and return status */ if (JpmcdsHolidayListAddBusinessDays (fromDate, offset, hl, result) != SUCCESS) goto done; status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** A convenience function for JpmcdsZerosToCouponsPointAdj with no bad-day ** adjustment. ** ** Calls JpmcdsZerosToCouponsPoint with badDayConv = JPMCDS_BAD_DAY_NONE. *************************************************************************** */ int JpmcdsZerosToCouponsPoint (TCurve *zc, /* (I) Zero-coupon rate curve */ long interpType, /* (I) */ TDate startDate, /* (I) Date instrument begins at */ TDateInterval *interval, /* (I) Time between payments */ TDate maturityDate, /* (I) Date instrument matures at*/ long fixedDCC, /* (I) See JpmcdsDayCountFraction */ TBoolean stubAtEnd, /* (I) TRUE=Back, FALSE=Front*/ double *couponRate) /* (O) Rate for this instrument */ { static char routine[]="JpmcdsZerosToCouponsPoint"; int status = FAILURE; /* Turn off bad day adjustment. */ if (JpmcdsZerosToCouponsPointAdj (zc, interpType, startDate, interval, maturityDate, fixedDCC, stubAtEnd, JPMCDS_BAD_DAY_NONE, JPMCDS_BAD_DAY_NONE, "NO_WEEKENDS", couponRate) == FAILURE) goto done; status = SUCCESS; done: if (status == FAILURE) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Computes the zero-coupon forward rate from startDate to the maturity ** date using a zero-coupon curve. ** ** Note the routine does not check to make sure that the start date comes ** before the maturity date, since zero curve creation requires the ** calculation of forwards which go backwards (JpmcdsDiscountToRate should ** handle that check anyway). *************************************************************************** */ int JpmcdsForwardFromZCurve( TCurve *zc, /* (I) Zero Curve */ long interpType, /* (I) Type of interpolation */ TDate startDate, /* (I) Start Date */ TDate endDate, /* (I) End Date */ long dayCountConv, /* (I) See ldate.h */ long basis, /* (I) See cfinanci.h */ double *rate) /* (O) rate from start to end */ { static char routine[]="JpmcdsForwardFromZCurve"; int status = FAILURE; /* Until proven successful */ double startDiscount; double endDiscount; double forwardDiscount; if (JpmcdsDiscountDate(startDate, zc, interpType, &startDiscount) == FAILURE) goto done; if (JpmcdsDiscountDate(endDate, zc, interpType, &endDiscount) == FAILURE) goto done; forwardDiscount = endDiscount/startDiscount; if (JpmcdsDiscountToRate(forwardDiscount, startDate, endDate, dayCountConv, basis, rate) == FAILURE) goto done; status = SUCCESS; done: if (status == FAILURE) JpmcdsErrMsg("%s: failed.\n", routine); return status; }
/* *************************************************************************** ** Loads a holiday cache entry from a disk file, removing old one if it ** exists. *************************************************************************** */ EXPORT int JpmcdsHolidayLoadFromDisk (char *name, /* (I) name associated with holidays */ char *filename) /* (I) filename to load */ { static char routine[] = "JpmcdsHolidayLoadFromDisk"; int status = FAILURE; THolidayList *hl; hl = JpmcdsHolidayListRead (filename); if (hl == NULL) goto done; if (JpmcdsHolidayListAddToCache (name, hl) != SUCCESS) goto done; status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Returns metric function to use for interpolation routines for a given ** day count convention. *************************************************************************** */ TMetricDoubleFunc JpmcdsDayCountToMetricFunc(long dayCountConv) { static char routine[]="JpmcdsDayCountToMetricFunc"; switch(dayCountConv) { case JPMCDS_ACT_365F: case JPMCDS_ACT_365: case JPMCDS_ACT_360: return (TMetricDoubleFunc) actualDaysDiff; /*NOTREACHED*/ case JPMCDS_B30_360: return (TMetricDoubleFunc) bondDaysDiff; /*NOTREACHED*/ case JPMCDS_B30E_360: return (TMetricDoubleFunc) bondEDaysDiff; /*NOTREACHED*/ default: JpmcdsErrMsg("%s: Day count convention %ld unknown.\n", routine, dayCountConv); return (TMetricDoubleFunc) NULL; } }
/* ********************************************************************* ** Creates a TStubPos from a long. ********************************************************************* */ int JpmcdsStubPosMake (long stubPosValue, /* (I) Number representing stub position */ TStubPos *stubPos) /* (O) Stub position */ { static char routine[] = "JpmcdsStubPosMake"; int status = FAILURE; switch(stubPosValue) { case JPMCDS_STUB_POS_DEFAULT_AUTO: *stubPos = JPMCDS_STUB_POS_DEFAULT_AUTO; break; case JPMCDS_STUB_POS_DEFAULT_FRONT: *stubPos = JPMCDS_STUB_POS_DEFAULT_FRONT; break; case JPMCDS_STUB_POS_DEFAULT_BACK: *stubPos = JPMCDS_STUB_POS_DEFAULT_BACK; break; default: JpmcdsErrMsg("%s: Unknown stub pos %ld.\n", routine, stubPosValue); goto done; } status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsgFailure(routine); return status; }
/* *************************************************************************** ** The function first checks if the input name is really an encoded ** pointer. If it is, the function decodes the pointer and returns ** it. Otherwise, it checks whether the name is "NONE" or "NO_WEEKENDS". ** If it is either of those, the function returns a pointer to an ** appropriate static data structure (for efficiency). If it is ** none of the above, then the function searches for the name in the ** cache. If it finds the name, it returns a pointer to the cached ** holiday list. Otherwise, it interprets the name as a file name ** and tries to load the holiday file from disk. ** ** You can use name = "NONE" or name = "NO_WEEKENDS" and the resulting ** holiday list will be defined appropriately. ** ** This behaviour exactly duplicates the behaviour when using a holiday ** name as an input to any analytics function. ** ** Returns NULL on failure, a valid THolidayList pointer on success. *************************************************************************** */ THolidayList* JpmcdsHolidayListFromCache (char *name) /* (I) Name associated with the holidays */ { static char routine[] = "JpmcdsHolidayListFromCache"; int status = FAILURE; THolidayList *hl = NULL; /* To be returned */ THoliday *hol; /* Must have a name. */ if (name == NULL) { JpmcdsErrMsg ("%s: NULL inputs.\n", routine); goto done; } /* Use holidayFind to find in the cache. Note that NONE and NO_WEEKENDS are always in the cache. */ hol = holidayFind (name); if (hol != NULL) { hl = hol->hl; } else { /* Not in the cache, so use JpmcdsHolidayListRead to read from file. */ hl = JpmcdsHolidayListRead (name); if (hl == NULL) goto done; /* Now add hl to the cache. This takes a shallow copy, but is guaranteed to be destructive if the operation fails. */ if (JpmcdsHolidayListAddToCache (name, hl) != SUCCESS) goto done; } status = SUCCESS; done: if (status != SUCCESS) { JpmcdsErrMsg ("%s: Failed.\n", routine); return NULL; } return hl; }
/* *************************************************************************** ** Retrieve data for addin object. Returns object (NULL if error). *************************************************************************** */ void* RetrieveObject(char* handle) { static char *routine = "RetrieveObject"; int status = FAILURE; void *data = NULL; char *name = NULL; TObject *node; char *sep; if (handle == NULL) { JpmcdsErrMsg("%s: No object handle specified.\n", routine); goto done; } name = strdup(handle); if (name == NULL) goto done; /* remove version number from lookup string */ sep = strchr(name, SEPARATOR); if (sep != NULL) *sep = '\0'; node = FindNode(name); if (node == NULL) { JpmcdsErrMsg("%s: No object called %s found.\n", routine, name); goto done; } data = node->data; status = SUCCESS; done: if (status != SUCCESS) { JpmcdsErrMsg("%s: Failed!\n", routine); data = NULL; } FREE(name); return data; }
/* *************************************************************************** ** 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; }
static int localUngetc(TFile *tFile, int c) { if (tFile->hasLastChar == TRUE) { JpmcdsErrMsg("Unexpected ungetchar.\n"); } tFile->lastChar = c; tFile->hasLastChar = TRUE; return 0; }
/* *************************************************************************** ** Creates a new THoliday pointer. Takes shallow copy of the holiday list, ** takes a deep capitalized copy of the name. *************************************************************************** */ static THoliday* JpmcdsNewHoliday (THolidayList *hl, /* (I) Holiday list - takes shallow copy */ char *name /* (I) Name - takes deep capitalized copy */ ) { static char routine[] = "JpmcdsNewHoliday"; int status = FAILURE; THoliday *hol = NULL; if (hl == NULL || name == NULL) { JpmcdsErrMsg ("%s: NULL inputs.\n", routine); goto done; } hol = NEW(THoliday); if (hol == NULL) goto done; hol->hl = hl; hol->name = JpmcdsStringDuplicate(name); hl = NULL; /* Now owned by hol */ if (hol->name == NULL) goto done; hol->next = NULL; status = SUCCESS; done: JpmcdsHolidayListDelete(hl); if (status != SUCCESS) { JpmcdsFreeHoliday (hol); hol = NULL; JpmcdsErrMsg ("%s: Failed.\n", routine); } return hol; }
/* *************************************************************************** ** Equivalent to JpmcdsIsHoliday, but uses a THolidayList structure instead of ** a holiday file name (and output type is TBoolean rather than long). ** ** Does not take week-ends into account. *************************************************************************** */ int JpmcdsHolidayListIsHoliday (TDate date, /* (I) Arbitrary date */ THolidayList *hl, /* (I) Holiday list structure */ TBoolean *isHoliday) /* (O) 0 = not a holiday, 1 = is a holiday */ { static char routine[] = "JpmcdsHolidayListIsHoliday"; TDate * dates; TDate * bRet; *isHoliday = FALSE; if (hl == NULL || hl->dateList == NULL) { JpmcdsErrMsg("%s: hl is NULL.\n", routine); return FAILURE; } if (IS_EMPTY_DATELIST( hl->dateList )) { return SUCCESS; } dates = hl->dateList->fArray ; if (dates == NULL) { /* Should only be possible if numItems == zero. */ PROGRAM_BUG(); return FAILURE; } /* ** Do a binary search of the date list to see if date ** is in the list. */ bRet = (TDate *) bsearch ((void *) &date, (void *) &dates[0], (size_t) hl->dateList->fNumItems, (size_t) sizeof(TDate), holidayCompare); if (bRet != NULL) /* found == holiday */ { *isHoliday = TRUE; } return SUCCESS; }
/* *************************************************************************** ** Makes a new empty TCashFlowList. *************************************************************************** */ TCashFlowList* JpmcdsNewEmptyCFL(int numItems) { static char routine[]="JpmcdsNewEmptyCFL"; TCashFlowList *cfl = NULL; if (numItems < 0) { JpmcdsErrMsg("%s: # cashflows (%d) must be >= 0.\n", routine, numItems); goto done; } cfl = NEW(TCashFlowList); if (cfl == NULL) goto done; cfl->fNumItems = numItems; if (numItems > 0) { cfl->fArray = NEW_ARRAY (TCashFlow, numItems); if (cfl->fArray == NULL) goto done; } else { cfl->fArray = NULL; } /* Success */ return cfl; done: JpmcdsErrMsg("%s: Failed.\n", routine); JpmcdsFreeCFL(cfl); return NULL; }
/* *************************************************************************** ** Calculates the coupon rate for an instrument, such that cashflows ** discounted by the zero-rates equal the present value. ** ** Uses: ** 1) zero-coupon rates in the zc structure, ** 2) cashFlowList, which contains coupons payments for an instrument based ** on an annual rate of 1 (100%), (in other words, fraction of annual ** coupon which should be paid at the given date) ** 3) the present Value of the instrument ** ** It is assumed that the last payment represents a coupon plus principal. *************************************************************************** */ static int JpmcdsCalcCoupon( TCurve *zc, /* (I) Zero-coupon rate curve */ long interpType, /* (I) Interpolation */ TCashFlowList *cfl, /* (I) Cash Flow list */ double presentValue, /* (I) Present value */ double *couponRate) /* (O) Annual coupon rate */ { static char routine[]="JpmcdsCalcCoupon"; int status = FAILURE; int num_pts = cfl->fNumItems; double coupons_PV; /* Present value of coupons */ double last_PV; /* Present value of final payment */ if (JpmcdsCashFlowPV(cfl, zc, interpType, &coupons_PV) == FAILURE) goto done; if (coupons_PV == 0.) { JpmcdsErrMsg("JpmcdsCalcCoupon: coupons have zero present value.\n"); goto done; } if (JpmcdsDiscountDate(cfl->fArray[num_pts-1].fDate, zc, interpType, &last_PV) == FAILURE) goto done; *couponRate = (presentValue - last_PV) / coupons_PV; status = SUCCESS; done: if (status == FAILURE) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Maps day count conventions to (private) strings. *************************************************************************** */ char* JpmcdsFormatDayCountConv(long dayCountConv) { static char routine[]="JpmcdsFormatDayCountConv"; switch(dayCountConv) { case JPMCDS_DEFAULT: return JPMCDS_DEFAULT_STR; /*NOTREACHED*/ case JPMCDS_ACT_365F: return JPMCDS_ACT_365F_STR; /*NOTREACHED*/ case JPMCDS_ACT_365: return JPMCDS_ACT_365_STR; /*NOTREACHED*/ case JPMCDS_ACT_360: return JPMCDS_ACT_360_STR; /*NOTREACHED*/ case JPMCDS_B30_360: return JPMCDS_B30_360_STR; /*NOTREACHED*/ case JPMCDS_B30E_360: return JPMCDS_B30E_360_STR; /*NOTREACHED*/ case JPMCDS_EFFECTIVE_RATE: return JPMCDS_EFFECTIVE_RATE_STR; /*NOTREACHED*/ default: goto done; } done: JpmcdsErrMsg("%s: Day count convention %ld unknown.\n", routine, dayCountConv); return "unrecognized"; /*NOTREACHED*/ }
/* *************************************************************************** ** Converts date to a valid business date using the holiday list and bad ** day convention passed in. ** ** Returns SUCCESS if convention OK, FAILURE otherwise. ** ** Equivalent to JpmcdsBusinessDay except using a THolidayList structure ** instead of a holiday file. Should be used if efficiency is an issue. *************************************************************************** */ int JpmcdsHolidayListBusinessDay (TDate date, /* (I) Arbitrary date. */ long badDayConv, /* (I) Bad day convention for adjusting non-business days. Use one of the following: JPMCDS_BAD_DAY_FOLLOW JPMCDS_BAD_DAY_PREVIOUS JPMCDS_BAD_DAY_MODIFIED JPMCDS_BAD_DAY_NONE */ THolidayList *hl, /* (I) Holiday file list. */ TDate *outDate) /* (O) Valid business day. */ { static char routine[] = "JpmcdsHolidayListBusinessDay"; int status; /* just pass call down to JpmcdsMultiHolidayListBusinessDay */ status = JpmcdsMultiHolidayListBusinessDay(date, badDayConv, 1, &hl, outDate); if (status != SUCCESS) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }
/* *************************************************************************** ** Using business day conventions (Following, Preceding, and Modified ** Following) calculates the next bisiness day if the input date is not a ** business day. Input and Output dates are represented as TDate types. *************************************************************************** */ int JpmcdsBusinessDay (TDate date, /* Input Date */ long method, /* See ldate.h */ char *holidayFile, /* Filename w/ Holidays */ TDate *outDate) /* output date */ { static char routine[] = "JpmcdsBusinessDay"; THolidayList *hl = NULL; int status = FAILURE; /* determine whether we should do anything */ if (method == JPMCDS_BAD_DAY_NONE) { *outDate = date; status = SUCCESS; goto done; /* success */ } /* if method is other than NONE, pass to hl routine */ hl = JpmcdsHolidayListFromCache (holidayFile); if (hl == NULL) goto done; /* call the real routine, and return status */ if (JpmcdsHolidayListBusinessDay (date, method, hl, outDate) != SUCCESS) goto done; status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg ("%s: Failed.\n", routine); return status; }
/*f *************************************************************************** ** Calculates a date being at a business or calendar days offset from the ** input date. *************************************************************************** */ int JpmcdsDtFwdAdj (TDate startDate, /* (I) input date */ TDateAdjIntvl *adjIvl, /* (I) in business or calendar days */ TDate *result) /* (O) resulting date */ { int status = FAILURE; static char routine[]="JpmcdsDtFwdAdj"; switch (adjIvl->isBusDays) { case JPMCDS_DATE_ADJ_TYPE_BUSINESS: /* offset by business days */ if (adjIvl->interval.prd_typ == 'D') { if (JpmcdsDateFromBusDaysOffset (startDate, adjIvl->interval.prd, /* Number days */ adjIvl->holidayFile, result) != SUCCESS) { goto done; } } else { /* ** Non-daily interval and offset by business days. ** This means that if the start date is the end of the business ** month, then the end date must also be the end of the month. ** Actually we do adjust the end date by the bad day convention, ** but in the context of generating a set of cash flow dates, ** this is often not done when computing the maturity date. */ TDate businessEOM; TDate endDate; if (JpmcdsDateToBusinessEOM (startDate, adjIvl->holidayFile, &businessEOM) != SUCCESS) { goto done; } if (JpmcdsDtFwdAny (startDate, &adjIvl->interval, &endDate) != SUCCESS) { goto done; } if (startDate == businessEOM) { if (JpmcdsDateToEOM (endDate, &endDate) != SUCCESS) goto done; } if (JpmcdsBusinessDay (endDate, adjIvl->badDayConv, adjIvl->holidayFile, result) != SUCCESS) { goto done; } } break; case JPMCDS_DATE_ADJ_TYPE_CALENDAR: /* offset by calendar period */ if (JpmcdsDateFwdThenAdjust (startDate, &adjIvl->interval, adjIvl->badDayConv, adjIvl->holidayFile, result) != SUCCESS) { goto done; } break; case JPMCDS_DATE_ADJ_TYPE_WEEKDAY: /* Adjust interval as weekdays, and then bad day adjust the result. */ if (adjIvl->interval.prd_typ == 'D') { if (JpmcdsDateFromBusDaysOffset (startDate, adjIvl->interval.prd, /* Number days */ "NONE", /* A holiday file with week-ends only */ result) != SUCCESS) { goto done; } if (JpmcdsBusinessDay (*result, adjIvl->badDayConv, adjIvl->holidayFile, result) != SUCCESS) { goto done; } } else { JpmcdsErrMsg ("%s: Weekday adjustment requires date interval " "%s to be in days.\n", routine, JpmcdsFormatDateInterval (&adjIvl->interval)); goto done; } break; default: JpmcdsErrMsg ("%s: Invalid date adjustment type %d.\n", routine, adjIvl->isBusDays); goto done; } status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }
/* ***************************************************************************** ** Adds a number of business days to the original date. ** ** Equivalent to JpmcdsDateFromBusDaysOffset, except using a THolidayList ** structure instead of a holiday file name. Should be used if efficiency ** is an issue. **************************************************************************** */ int JpmcdsHolidayListAddBusinessDays (TDate fromDate, /* (I) Input date */ long numBusDays, /* (I) Number of business days */ THolidayList *hl, /* (I) Holiday list structure */ TDate *resultDate) /* (O) Resulting business date */ { static char routine[] = "JpmcdsHolidayListAddBusinessDays"; int status = FAILURE; long intervalSign = SIGN( numBusDays ); long numBusDaysLeft = ABS( numBusDays ); long busDaysPerWeek = -1; if (hl == NULL) { JpmcdsErrMsg ("%s: hl is NULL.\n", routine); goto done; } /* ** Get the number of business days per week. In-line for speed. */ switch (hl->weekends) { case JPMCDS_WEEKEND_STANDARD: busDaysPerWeek = 5; break; case JPMCDS_WEEKEND_NO_WEEKENDS: busDaysPerWeek = 7; break; default: busDaysPerWeek = 7; if (hl->weekends & JPMCDS_WEEKEND_SUNDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_MONDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_TUESDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_WEDNESDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_THURSDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_FRIDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_SATURDAY) busDaysPerWeek--; break; } if (IS_EMPTY_DATELIST (hl->dateList) && hl->weekends == JPMCDS_WEEKEND_NO_WEEKENDS) { /* ** No adjustments at all. */ *resultDate = fromDate + (intervalSign * numBusDaysLeft); } else if (intervalSign == 1 && hl->weekends == JPMCDS_WEEKEND_STANDARD) { /* First, check for special case. The code for this case * was provided by Tom Robbins-Milne. It is much faster * than the general algorithm. */ if (numBusDaysLeft == 0) { *resultDate = fromDate; } else { /* * Calculate result if no holidays are involved. * * Move forward over a week for every 5 business days. * Use a table for moving 0..4 bus days from each day of the week. */ *resultDate = fromDate + 7 * (numBusDaysLeft / 5) + offsetTable[ fromDate % 7 ][ numBusDaysLeft % 5 ]; /* * Now adjust for any holidays */ if (hl->dateList->fNumItems > 0) { if (holAdjustStandardWeekends (fromDate, hl->dateList, resultDate) != SUCCESS) goto done; } } } else { if (forwardNonStandardWeekends (fromDate, numBusDaysLeft, intervalSign, hl->weekends, busDaysPerWeek, hl->dateList, resultDate) != SUCCESS) goto done; } status = SUCCESS; done: if (status == FAILURE) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }
/* ***************************************************************************** ** Calculates the number of business days between two dates (FROM & TO). ** ** Algorithm: ** 1. if FROM = TO, the result is 0 ** 2. if FROM < TO, the result is the number of business days in the CLOSED ** interval of [FROM+1,TO] ** 3. if FROM > TO, the result is negated number of business days in the ** CLOSED interval of [TO,FROM-1] ** ** Equivalent to JpmcdsBusinessDaysDiff but uses a THolidayList structure ** instead of a holiday file name. Should be used if efficiency is an ** issue. **************************************************************************** */ int JpmcdsHolidayListBusinessDaysDiff (TDate fromDate, /* (I) Earlier date */ TDate toDate, /* (I) Later date */ THolidayList *hl, /* (I) Holiday list structure */ long *result) /* (O) Result value */ { static char routine[] = "JpmcdsHolidayListBusinessDaysDiff"; int busDaysPerWeek = -1; long nrExtraDays; long numWeeks = 0L; int signum = 1; /* assume going forward by default */ long numHolidays = 0L; TDate curDate = fromDate; /* * The following tables are used to calculate the number of business days * between weekdays. * If (toDate > fromDate) use fwdDiffTable. * eg NumDays = fwdDiffTable[ fromDate % 7 ][ toDate % 7 ] * * If (fromDate > toDate) use -bwdDiffTable. * eg NumDays = bwdDiffTable[ toDate % 7 ][ fromDate % 7 ] * * The values of the table were chosen so that the current ALIB behaviour * was left unchanged. Note that because fwdDiffTable is not identical * to (-1 x bwdDiffTable), swapping fromDate and toDate in the call to this * function does not simply reverse the sign of the result. */ static long fwdDiffTable[7][7] = {/* Mo Tu We Th Fr Sa Su*/ { 0, 1, 2, 3, 4, 4, 4 }, /* Monday */ { 4, 0, 1, 2, 3, 3, 3 }, /* Tuesday */ { 3, 4, 0, 1, 2, 2, 2 }, /* Wednesday */ { 2, 3, 4, 0, 1, 1, 1 }, /* Thursday */ { 1, 2, 3, 4, 0, 0, 0 }, /* Friday */ { 1, 2, 3, 4, 5, 0, 0 }, /* Saturday */ { 1, 2, 3, 4, 5, 5, 0 } /* Sunday */ }; static long bwdDiffTable[7][7] = {/* Mo Tu We Th Fr Sa Su*/ { 0,-1,-2,-3,-4,-5,-5 }, /* Monday */ { -4, 0,-1,-2,-3,-4,-4 }, /* Tuesday */ { -3,-4, 0,-1,-2,-3,-3 }, /* Wednesday */ { -2,-3,-4, 0,-1,-2,-2 }, /* Thursday */ { -1,-2,-3,-4, 0,-1,-1 }, /* Friday */ { -0,-1,-2,-3,-4, 0, 0 }, /* Saturday */ { -0,-1,-2,-3,-4,-5, 0 } /* Sunday */ }; if (hl == NULL) { JpmcdsErrMsg ("%s: hl is NULL.\n", routine); return FAILURE; } *result = 0L; /* TDate EQ long */ if (fromDate == toDate) return SUCCESS; /* Set the direction. */ if (toDate < fromDate) { signum = -1; } /* ** First, compute the date difference adjusting for weekends only. */ /* ** Get the number of business days per week. In-line for speed. */ switch (hl->weekends) { case JPMCDS_WEEKEND_STANDARD: busDaysPerWeek = 5; numWeeks = (toDate - fromDate) / JPMCDS_DAYS_PER_WEEK; curDate += JPMCDS_DAYS_PER_WEEK * numWeeks; if (curDate > toDate) /* backwards */ { nrExtraDays = bwdDiffTable [ toDate %7 ][ fromDate % 7 ]; } else { nrExtraDays = fwdDiffTable [ fromDate %7 ][ toDate % 7 ]; } *result = numWeeks * busDaysPerWeek + nrExtraDays; break; case JPMCDS_WEEKEND_NO_WEEKENDS: *result = toDate - fromDate; break; default: busDaysPerWeek = 7; if (hl->weekends & JPMCDS_WEEKEND_SUNDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_MONDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_TUESDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_WEDNESDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_THURSDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_FRIDAY) busDaysPerWeek--; if (hl->weekends & JPMCDS_WEEKEND_SATURDAY) busDaysPerWeek--; numWeeks = ABS((toDate - fromDate) / JPMCDS_DAYS_PER_WEEK); curDate += (JPMCDS_DAYS_PER_WEEK * numWeeks * signum); nrExtraDays = 0L; while ( ABS(curDate - toDate) > 0 ) { curDate += signum; if (JPMCDS_IS_WEEKDAY( curDate, hl->weekends )) { nrExtraDays++; } } (*result) = ((numWeeks * busDaysPerWeek) + nrExtraDays) * signum; break; } /* ** Now count the number of weekday holidays ** and adjust the date difference by the result. The ** call to calcNumWeekdayHolidays searches the ** holiday list at most once. */ if (! IS_EMPTY_DATELIST( hl->dateList )) { TBoolean doneSearching = FALSE; long holIdx = -1; if (findFirstHolidayIdx (fromDate + signum, hl->dateList, signum, &holIdx, &doneSearching) != SUCCESS) return FAILURE; if (!doneSearching) { numHolidays = calcNumWeekdayHolidays (toDate, holIdx, signum, hl->weekends, hl->dateList, &holIdx); } } (*result) -= numHolidays * signum; return SUCCESS; }
/* *************************************************************************** ** Compute the date of next business day, given a start date and ** a holiday list. The function currently always returns SUCCESS. *************************************************************************** */ static int getNextBusDate (TDate startDate, /* (I) starting date. */ long direction, /* (I) +1=forwards, -1=backwards */ THolidayList * hl, /* (I) holiday list */ TDate * nextDate /* (O) next business day */ ) { TBoolean doneSearchingList; TBoolean aHoliday; long holIdx; long numHols = hl->dateList->fNumItems; TDate * holArray = hl->dateList->fArray; TDate curDate = startDate; if (IS_EMPTY_DATELIST(hl->dateList)) { doneSearchingList = TRUE; } else { if (findFirstHolidayIdx (startDate, hl->dateList, direction, &holIdx, &doneSearchingList) != SUCCESS) { JpmcdsErrMsg ("getNextBusDate: Failed.\n"); return FAILURE; } } /* ** holIdx should now point to the first holiday on or before ** (after) the start date. Now loop though each day until ** you reach the first business day. */ aHoliday = TRUE; do { /* Check if it's a holiday first. */ if (!doneSearchingList && curDate == holArray[ holIdx ]) { holIdx += direction; curDate += direction; doneSearchingList = (TBoolean)(holIdx < 0 || holIdx >= numHols); } else { /* Day is not a holiday. Check if it's a week-end day. */ if (JPMCDS_IS_WEEKEND (curDate, hl->weekends)) { curDate += direction; } else { aHoliday = FALSE; } } } while ( aHoliday == TRUE ); *nextDate = curDate; return SUCCESS; }
/* *************************************************************************** ** (See getNextBusDate.) *************************************************************************** */ static int getNextBusDateMulti (TDate startDate, /* (I) starting date. */ long direction, /* (I) +1=forwards, -1=backwards */ int numHolidayLists, /* (I) Size of holidayLists[] array. */ THolidayList *holidayLists[], /* (I) Holiday lists. */ TDate *nextDate /* (O) next business day */ ) { static char routine[] = "getNextBusDateMulti"; int status = FAILURE; int n; TDate adjDate; *nextDate = startDate; /* this loop executes getNextBusDate for each market in sequence; whenever weekends/ holidays force an adjustment, the process recommences at the first market; eventually, a date will be reached which is a business day in all markets, whereupon the loop will execute to completion we can be certain that all of the skipped dates were weekends/holidays in at least one of the markets, i.e. output will be the _first_ suitable day */ n = 0; while (n < numHolidayLists) { if ( getNextBusDate(*nextDate, direction, holidayLists[n], &adjDate) != SUCCESS ) { goto done; } if ( adjDate != *nextDate ) { *nextDate = adjDate; /* date has changed to acommodate this market; now have to recommence testing for all other markets */ /* in fact, this date does not need retesting for the current market, and we exploit this in the special case below, which avoids a redundant duplicate test in "single holiday list" usage */ n = ( n == 0 ) ? 1 : 0; /* ^-- if adjusted for first market, now move on to second; in all other cases, recommence testing from scratch */ } else { n++; /* next market */ } } status = SUCCESS; done: if (status != SUCCESS) JpmcdsErrMsg("%s: Failed.\n", routine); return status; }