File indexing completed on 2024-05-12 16:35:31

0001 /* This file is part of the KDE project
0002    Copyright (C) 2007 Sascha Pfau <MrPeacock@gmail.com>
0003    Copyright (C) 1998-2002 The KSpread Team <calligra-devel@kde.org>
0004 
0005    This library is free software; you can redistribute it and/or
0006    modify it under the terms of the GNU Library General Public
0007    License as published by the Free Software Foundation; only
0008    version 2 of the License.
0009 
0010    This library is distributed in the hope that it will be useful,
0011    but WITHOUT ANY WARRANTY; without even the implied warranty of
0012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0013    Library General Public License for more details.
0014 
0015    You should have received a copy of the GNU Library General Public License
0016    along with this library; see the file COPYING.LIB.  If not, write to
0017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
0018    Boston, MA 02110-1301, USA.
0019 */
0020 
0021 // Local
0022 #include "helper.h"
0023 
0024 #include "SheetsDebug.h"
0025 
0026 #include <QDate>
0027 
0028 #include <math.h>
0029 
0030 /*  DISABLED - we use KCalendarSystem instead
0031 void addMonths( QDate & date, int months )
0032 {
0033   int d = date.day();
0034   int m = date.month() + months;
0035   int y = date.year();
0036 
0037   if ( m > 12 )
0038   {
0039     y += (int) ( m / 12 );
0040     m %= 12;
0041   }
0042 
0043   // e.g. 31 Feb: decrease day...
0044   while ( !QDate::isValid( y, m, d ) && d > 0 )
0045     --d;
0046 
0047   date.setYMD( y, m, d );
0048 }
0049 
0050 void subMonths( QDate & date, int months )
0051 {
0052   int d = date.day();
0053   int m = date.month() - months;
0054   int y = date.year();
0055 
0056   while ( m < 1 )
0057   {
0058     m += 12;
0059     y -= 1;
0060   }
0061 
0062   // e.g. 31 Feb: decrease day
0063   while ( !QDate::isValid( y, m, d ) && d > 0 )
0064     --d;
0065 
0066   date.setYMD( y, m, d );
0067 }
0068 
0069 */
0070 
0071 int Calligra::Sheets::daysPerYear(QDate const & date, int basis)
0072 {
0073     switch (basis) {
0074     case 0:
0075         return 360;
0076 
0077     case 1:
0078         if (QDate::isLeapYear(date.year()))
0079             return 366;
0080         return 365;
0081 
0082     case 2:
0083         return 360;
0084     case 3:
0085         return 365;
0086     case 4:
0087         return 360;
0088     }
0089 
0090     return -1;
0091 }
0092 
0093 int Calligra::Sheets::daysBetweenDates(QDate const & date1, QDate const & date2, int basis)
0094 {
0095     int day1, day2, month1, month2, year1, year2;
0096     bool isLeapYear = false;
0097     int days, months, years;
0098 
0099     day1   = date1.day();
0100     month1 = date1.month();
0101     year1  = date1.year();
0102     day2   = date2.day();
0103     month2 = date2.month();
0104     year2  = date2.year();
0105 
0106     years  = year2  - year1;
0107     months = month2 - month1 + years * 12;
0108     days   = day2   - day1;
0109 
0110     isLeapYear = QDate::isLeapYear(year1);
0111 
0112     switch (basis) {
0113     case 0:
0114         if (month1 == 2 && month2 != 2 && year1 == year2) {
0115             if (isLeapYear)
0116                 return months * 30 + days - 1;
0117             else
0118                 return months * 30 + days - 2;
0119         }
0120         return months * 30 + days;
0121 
0122     case 1: // TODO: real days for difference between months!
0123         //    return ( month2 - month1 ) * 30 + years * 360 + days;
0124 
0125     case 2: // TODO: real days for difference between months!
0126         //    return ( month2 - month1 ) * 30 + years * 365 + days;
0127 
0128     case 3:
0129         return date1.daysTo(date2);
0130 
0131     case 4:
0132         return months * 30 + days;
0133     }
0134 
0135     return -1;
0136 }
0137 
0138 // the days360 method does implement the 30/360days method as used in e.g. the YEARFRAC function
0139 int Calligra::Sheets::days360(int day1, int month1, int year1, bool leapYear1,
0140                      int day2, int month2, int year2, bool leapYear2,
0141                      bool usaMethod)
0142 {
0143     if (usaMethod) { // US method
0144         if (day1 == 31) {
0145             day1 = 30;
0146             if (day2 == 31) {
0147                 day2 = 30;
0148             }
0149         }
0150         else if (day1 == 30 && day2 == 31) {
0151             day2 = 30;
0152         }
0153         else if (month1 == 2 && (day1 == 29 || (day1 == 28 && ! leapYear1))) {
0154             day1 = 30;
0155             if (month2 == 2 && (day2 == 29 || (day2 == 28 && ! leapYear2))) {
0156                 day2 = 30;
0157             }
0158         }
0159     } else { // European method
0160         if (day1 == 31) {
0161             day1 = 30;
0162         }
0163         if (day2 == 31) {
0164             day2 = 30;
0165         }
0166     }
0167     return day2 + month2 * 30 + year2 * 360 - day1 - month1 * 30 - year1 * 360;
0168 }
0169 
0170 
0171 // days360
0172 int Calligra::Sheets::days360(const QDate& _date1, const QDate& _date2, bool european)
0173 {
0174     int day1, month1, year1, day2, month2, year2;
0175 
0176     day1 = _date1.day();
0177     month1 = _date1.month();
0178     year1 = _date1.year();
0179 
0180     day2 = _date2.day();
0181     month2 = _date2.month();
0182     year2 = _date2.year();
0183 
0184     return days360(day1, month1, year1, QDate::isLeapYear(_date1.year()), day2, month2, year2, QDate::isLeapYear(_date2.year()), !european);
0185 }
0186 
0187 
0188 
0189 
0190 // // days360
0191 // int Calligra::Sheets::days360( const QDate& _date1, const QDate& _date2, bool european )
0192 // {
0193 //   int day1, day2;
0194 //   int month1, month2;
0195 //   int year1, year2;
0196 //   bool negative = false;
0197 //   QDate date1( _date1 );
0198 //   QDate date2( _date2 );
0199 //
0200 //   if (date1.daysTo( date2 ) < 0)
0201 //   {
0202 //     QDate tmp( date1 );
0203 //     date1 = date2;
0204 //     date2 = tmp;
0205 //     negative = true;
0206 //   }
0207 //
0208 //   day1   = date1.day();
0209 //   day2   = date2.day();
0210 //   month1 = date1.month();
0211 //   month2 = date2.month();
0212 //   year1  = date1.year();
0213 //   year2  = date2.year();
0214 //
0215 //   if ( european )
0216 //   {
0217 //     if ( day1 == 31 )
0218 //       day1 = 30;
0219 //     if ( day2 == 31 )
0220 //       day2 = 30;
0221 //   }
0222 //   else
0223 //   {
0224 //     // thanks to the Gnumeric developers for this...
0225 //     if ( month1 == 2 && month2 == 2
0226 //          && date1.daysInMonth() == day1
0227 //          && date2.daysInMonth() == day2 )
0228 //       day2 = 30;
0229 //
0230 //     if ( month1 == 2 && date1.daysInMonth() == day1 )
0231 //       day1 = 30;
0232 //
0233 //     if ( day2 == 31 && day1 >= 30 )
0234 //       day2 = 30;
0235 //
0236 //     if ( day1 == 31 )
0237 //       day1 = 30;
0238 //   }
0239 //
0240 //   return ( ( year2 - year1 ) * 12 + ( month2 - month1 ) ) * 30
0241 //     + ( day2 - day1 );
0242 // }
0243 
0244 // yearFrac
0245 long double Calligra::Sheets::yearFrac(const QDate& refDate, const QDate& startDate, const QDate& endDate, int basis)
0246 {
0247     Q_UNUSED(refDate);
0248     QDate date1 = startDate;
0249     QDate date2 = endDate;
0250 
0251     //
0252     // calculation
0253     //
0254 
0255     if (date2 < date1) {
0256         // exchange dates
0257         QDate Temp1 = date1;
0258         date1 = date2;
0259         date2 = Temp1;
0260     }
0261 
0262     int days = date1.daysTo(date2);
0263 
0264 //   debugSheetsFormula <<"date1 =" << date1 <<"    date2 =" << date2 <<"    days =" << days <<"    basis =" << basis;
0265 
0266     long double res = 0;
0267     long double peryear = 0;
0268     int nYears = 0;
0269 
0270     switch (basis) {
0271     case 1: {
0272         nYears = date2.year() - date1.year() + 1;
0273         for (int y = date1.year(); y <= date2.year(); ++y) {
0274             peryear += QDate::isLeapYear(y) ? 366 : 365;
0275         }
0276         // less than one year - even if it does span two consequentive years ...
0277         if (QDate(date1.year() + 1, date1.month(), date1.day()) >= date2) {
0278             nYears = 1;
0279             peryear = 365;
0280             if (QDate::isLeapYear(date1.year()) && date1.month() <= 2) peryear = 366;
0281             else if (QDate::isLeapYear(date2.year()) && date2.month() > 2) peryear = 366;
0282             else if (date2.month() == 2 && date2.day() == 29) peryear = 366;
0283         }
0284         peryear = peryear / (long double) nYears;
0285         nYears = 0;
0286         break;
0287     }
0288     case 2: {
0289         // Actual/360
0290         peryear = 360;
0291         break;
0292     }
0293     case 3: {
0294         // Actual/365
0295         peryear = 365;
0296         break;
0297     }
0298     case 4: {
0299         // 30/360 Europe
0300 
0301         // calc datedif360 (start, end, Europe)
0302         days = days360(date1, date2, 1);
0303 
0304         peryear = 360;
0305         break;
0306     }
0307     default: {
0308         // NASD 30/360
0309         //basis = 0;
0310 
0311         // calc datedif360 (start, end, US)
0312         days = days360(date1, date2, 0);
0313 
0314         peryear = 360;
0315     }
0316     }
0317 
0318     res = (long double)(nYears) + (long double)days / (long double) peryear;
0319 //   debugSheetsFormula<<"getYearFrac res="<<res;
0320     return res;
0321 }
0322 
0323 // pow1p calculate (1+x)^y accurately
0324 long double Calligra::Sheets::pow1p(const long double& x, const long double& y)
0325 {
0326     if (fabs(x) > 0.5)
0327         return pow(1 + x, y);
0328     else
0329         return exp(y * log1p(x));
0330 }
0331 
0332 // pow1pm1 calculate ((1+x)^y)-1 accurately
0333 long double Calligra::Sheets::pow1pm1(const long double& x, const long double& y)
0334 {
0335     if (x <= -1)
0336         return pow(1 + x, y) - 1;
0337     else
0338         return expm1(y * log1p(x));
0339 }
0340 
0341 long double Calligra::Sheets::duration(const QDate& refDate, const QDate& settlement, const QDate& maturity,
0342                               const long double& coup_, const long double& yield_, const int& freq, const int& basis, const long double& numOfCoups)
0343 {
0344     long double yield = yield_;
0345     long double coup = coup_;
0346 
0347 //   debugSheetsFormula<<"DURATION_HELPER";
0348 //   debugSheetsFormula<<"sett ="<<settlement<<" mat ="<<maturity<<" coup ="<<coup<<" yield ="<<yield<<" freq ="<<freq<<" basis ="<<basis;
0349 
0350 
0351     long double yearfrac = yearFrac(refDate, settlement, maturity, basis);
0352     long double res = 0.0l;
0353     const long double f100 = 100.0l;
0354     coup *= f100 / (long double)(freq);
0355 
0356     yield /= freq;
0357     yield += 1.0;
0358 
0359     long double diff = yearfrac * freq - numOfCoups;
0360 
0361     long double t;
0362 
0363     for (t = 1.0l ; t < numOfCoups ; t += 1.0l)
0364         res += (t + diff) * (coup) / pow(yield, t + diff);
0365 
0366     res += (numOfCoups + diff) * (coup + f100) / pow(yield, numOfCoups + diff);
0367 
0368     long double p = 0.0l;
0369     for (t = 1.0l ; t < numOfCoups ; t += 1.0l)
0370         p += coup / pow(yield, t + diff);
0371 
0372     p += (coup + f100) / pow(yield, numOfCoups + diff);
0373 
0374     res /= p;
0375     res /= (long double)(freq);
0376 
0377     return(res);
0378 }