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 }