COUP*
- From: Neil Booth <neil daikokuya demon co uk>
- To: gnumeric-list gnome org, "Andreas J. Guelzow" <aguelzow taliesin ca>
- Cc: jody daikokuya demon co uk
- Subject: COUP*
- Date: Wed, 16 Jan 2002 08:00:01 +0000
Here's my suggestion for getting bases done and dusted. I have not
had much time to test it, so be warned.
I think it's quite clean; it removes about 50 lines of code, makes us
compatible with Excel for Windows (to the best of my knowledge) and is
quite clear how day counts work.
Neil.
2002-01-16 Neil Booth <neil daikokuya demon co uk>
* datetime.c (adjust_dates_basis): Remove.
(days_between_dep_basis): Remove.
(day_count): New function.
(coupdays): Return float, use day_count.
(coupdaysbs, coupdaysnc): Use day_count.
* datetime.h (basis_t): Document, and add convention 30E+/360.
(adjust_dates_basis, days_between_dep_basis): Remove.
(day_count): Update.
Index: datetime.c
===================================================================
RCS file: /cvs/gnome/gnumeric/src/datetime.c,v
retrieving revision 1.18
diff -u -p -r1.18 datetime.c
--- datetime.c 2002/01/15 06:17:04 1.18
+++ datetime.c 2002/01/16 07:46:49
@@ -308,117 +308,66 @@ datetime_weeknum (GDate *date, int metho
/* ------------------------------------------------------------------------- */
/*
- * adjust_dates_basis
+ * day_count
*
* @from : GDate *
* @to : GDate *
- * @basis : int
- * "0 US 30/360\n"
- * "1 actual days/actual days\n"
- * "2 actual days/360\n"
- * "3 actual days/365\n"
- * "4 European 30/360\n"
+ * @basis : basis_t
*
- * returns : nothing. As side effects possibly adjusts from and to date
- *
- *
- */
-
-void
-adjust_dates_basis (GDate *from, GDate *to, int basis)
-{
- switch (basis) {
- case BASIS_30Ep360:
- if (g_date_day(to) == 31)
- g_date_set_day (to, 30);
- break;
- case BASIS_30_360:
- if (g_date_day(from) >= 30) {
- g_date_set_day (from, 30);
- if (g_date_day(to) == 31)
- g_date_set_day (to, 30);
- }
- break;
- case BASIS_30E360:
- if (g_date_day(from) == 31)
- g_date_set_day (from, 30);
- if (g_date_day(to) == 31)
- g_date_set_day (to, 30);
- break;
- default:
- break;
- }
- return;
-}
-
-/* ------------------------------------------------------------------------- */
-
-/*
- * days_between_dep_basis
- *
- * @from : GDate *
- * @to : GDate *
- * @basis : int
- * "0 US 30/360\n"
- * "1 actual days/actual days\n"
- * "2 actual days/360\n"
- * "3 actual days/365\n"
- * "4 European 30/360\n"
- *
- * returns : Number of days strictly between from and to +1
- *
- * Note: before calling this function adjust_dates_basis _must_
- * have been called.
+ * returns : Number of days between from and to under the day count
+ * convention @basis.
*/
gint32
-days_between_dep_basis (GDate *from, GDate *to, int basis)
+day_count (GDate *from, GDate *to, basis_t basis)
{
- GDate from_date;
- gint32 days;
- gint years;
-
- switch (g_date_compare (from, to)) {
- case 1:
- return (- days_between_dep_basis (to, from, basis));
- case 0:
- return 0;
- default:
- break;
- }
-
- if (basis == BASIS_ACTACT)
- return (g_date_julian (to) - g_date_julian (from));
-
- g_date_clear (&from_date, 1);
- g_date_set_julian (&from_date, g_date_julian (from));
- years = g_date_year(to) - g_date_year(from);
- g_date_add_years (&from_date, years);
-
- if ((basis == BASIS_ACT365 || basis == BASIS_ACT360) &&
- g_date_compare (&from_date, to) >= 0) {
- years -= 1;
- g_date_set_julian (&from_date, g_date_julian (from));
- g_date_add_years (&from_date, years);
- }
+ int d1, m1, y1, d2, m2, y2;
- switch (basis) {
- case BASIS_ACT365:
- days = years * 365;
- break;
- default:
- days = years * 360;
- break;
+ /* ACT in the numerator means the number of days. */
+ if (basis == BASIS_ACTACT
+ || basis == BASIS_ACT360
+ || basis == BASIS_ACT365)
+ return datetime_g_days_between (from, to);
+
+ /* The rest are the four 30/360-style bases. */
+ d1 = g_date_day (from);
+ m1 = g_date_month (from);
+ y1 = g_date_day (from);
+
+ d2 = g_date_day (to);
+ m2 = g_date_month (to);
+ y2 = g_date_day (to);
+
+ /* Deal with this broken Excel-specific basis. */
+ if (basis == BASIS_30N360)
+ {
+ if (d1 == 31 && d2 >= 30)
+ d1 = 30;
+ if (d2 == 31 || (m2 == 2 && d2 >= 28))
+ d2 = 30;
+ }
+ else
+ {
+ if (d1 == 31)
+ d1 = 30;
+
+ if (d2 == 31)
+ {
+ /* Only 30E/360 is unconditional, the others
+ require d1 == 30. */
+ if (basis == BASIS_30E360)
+ d2 = 30;
+ else if (d1 == 30)
+ {
+ if (basis == BASIS_30Ep360)
+ d2 = 1, m2++;
+ else
+ d2 = 30;
+ }
+ }
}
- switch (basis) {
- case BASIS_ACT360:
- case BASIS_ACT365:
- return days + g_date_julian (to) - g_date_julian (&from_date);
- default:
- return (days + 30 * (g_date_month(to) - g_date_month(&from_date))
- + g_date_day(to) - g_date_day(&from_date));
- }
+ return ((y2 - y1) * 12 + (m2 - m1)) * 30 + (d2 - d1);
}
/* ------------------------------------------------------------------------- */
@@ -526,57 +475,57 @@ coup_cd_xl (GDate *settlement, GDate *ma
/*
- * Returns the number of days in the coupon period of the settlement date.
- * Currently, returns negative numbers if the branch is not implemented.
+ * Returns the number of days in the coupon period of the settlement
+ * date.
*/
-int
-coupdays (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl)
+gfloat
+coupdays (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl)
{
- GDate *prev;
- GDate *next;
- int days;
-
- switch (basis) {
- case BASIS_30Ep360:
- case BASIS_30E360:
- case BASIS_30_360:
- return (12 / freq) * 30;
- default:
- break;
- }
+ GDate *prev, *next;
+ gint32 result;
-/* Now we need to look at the real coupon dates */
+ switch (basis)
+ {
+ case BASIS_ACT365:
+ return 365.0 / freq;
+ case BASIS_30N360:
+ case BASIS_ACT360:
+ case BASIS_30E360:
+ case BASIS_30_360:
+ case BASIS_30Ep360:
+ return 360.0 / freq;
+ case BASIS_ACTACT:
+ break;
+ }
+ /* Act/Act is the only "interesting" case. */
prev = xl ? coup_cd_xl (settlement, maturity, freq, FALSE) :
coup_cd (settlement, maturity, freq, oem, FALSE);
next = xl ? coup_cd_xl (settlement, maturity, freq, TRUE) :
coup_cd (settlement, maturity, freq, oem, TRUE);
- adjust_dates_basis (prev, next, basis);
- days = days_between_dep_basis (prev, next, basis);
+ result = datetime_g_days_between (prev, next) / (gfloat) freq;
g_date_free (prev);
g_date_free (next);
- return days;
+ return result;
}
/* ------------------------------------------------------------------------- */
-
/*
* Returns the number of days from the beginning of the coupon period to
* the settlement date.
*/
int
-coupdaybs (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl)
+coupdaybs (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl)
{
GDate *prev_coupon;
int days;
prev_coupon = xl ? coup_cd_xl (settlement, maturity, freq, FALSE) :
coup_cd (settlement, maturity, freq, oem, FALSE);
- adjust_dates_basis (settlement, prev_coupon, basis);
- days = - days_between_dep_basis (settlement, prev_coupon, basis);
+ days = day_count (prev_coupon, settlement, basis);
g_date_free (prev_coupon);
return days;
}
@@ -590,16 +539,15 @@ coupdaybs (GDate *settlement, GDate *mat
*/
int
-coupdaysnc (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl)
+coupdaysnc (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl)
{
GDate *next_coupon;
int days;
next_coupon = xl ? coup_cd_xl (settlement, maturity, freq, TRUE) :
coup_cd (settlement, maturity, freq, oem, TRUE);
- adjust_dates_basis (settlement, next_coupon, basis);
- days = days_between_dep_basis (settlement, next_coupon, basis);
+ days = day_count (settlement, next_coupon, basis);
g_date_free (next_coupon);
return days;
}
Index: datetime.h
===================================================================
RCS file: /cvs/gnome/gnumeric/src/datetime.h,v
retrieving revision 1.9
diff -u -p -r1.9 datetime.h
--- datetime.h 2002/01/15 06:17:05 1.9
+++ datetime.h 2002/01/16 07:46:49
@@ -57,30 +57,42 @@ int datetime_g_years_between (GDate *dat
/* week number according to the given method. */
int datetime_weeknum (GDate *date, int method);
+/* 30N360 is what Excel calls 30/360 (NASD). For d2, it puts 31->30 and
+ last-day-of-Feb->30, and for d1 sets 31->30 if original d2 >= 30.
+ It is unused in today's financial markets to the best of my knowledge.
+ Act/Act is actual-number-of-days / actual-number-of-days.
+ Act/360 is actual-number-of-days / 360.
+ Act/365 is actual-number-of-days / 365.
+ 30E360 puts 31->30 for both dates.
+ 30_360 puts 31->30 for d1, and d2 puts 31->30 iff d1 >= 30.
+ 30Ep360 puts 31->30 for d1, and if d1 is >= 30 and d2 is 31, sets
+ d2->1 and increments m2.
+*/
+
typedef enum {
- BASIS_30Ep360 = 0, /* first date untouched, second date 31->30 */
+ BASIS_30N360 = 0,
BASIS_ACTACT = 1,
BASIS_ACT360 = 2,
BASIS_ACT365 = 3,
- BASIS_30E360 = 4, /* 31->30 for both dates */
- BASIS_30_360 = 5 /* 31->30 for first date, 31->30 if first date is >= 30 */
+ BASIS_30E360 = 4,
+ BASIS_30_360 = 5,
+ BASIS_30Ep360 = 6
} basis_t;
-
-void adjust_dates_basis (GDate *from, GDate *to, int basis);
-
-gint32 days_between_dep_basis (GDate *from, GDate *to, int basis);
-GDate * coup_cd (GDate *settlement, GDate *maturity, int freq, gboolean oem, gboolean next);
-GDate * coup_cd_xl (GDate *settlement, GDate *maturity, int freq, gboolean next);
+gint32 day_count (GDate *from, GDate *to, basis_t basis);
-int coupdays (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl);
+GDate * coup_cd (GDate *settlement, GDate *maturity, int freq, gboolean oem,
+ gboolean next);
+GDate * coup_cd_xl (GDate *settlement, GDate *maturity, int freq,
+ gboolean next);
-int coupdaybs (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl);
+gfloat coupdays (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl);
-int coupdaysnc (GDate *settlement, GDate *maturity, int freq, int basis, gboolean oem,
- gboolean xl);
+int coupdaybs (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl);
+int coupdaysnc (GDate *settlement, GDate *maturity, int freq, basis_t basis,
+ gboolean oem, gboolean xl);
#endif
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]