kspread

helper.cpp

Go to the documentation of this file.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2007 Sascha Pfau <MrPeacock@gmail.com>
00003    Copyright (C) 1998-2002 The KSpread Team <koffice-devel@kde.org>
00004 
00005    This library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Library General Public
00007    License as published by the Free Software Foundation; only
00008    version 2 of the License.
00009 
00010    This library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Library General Public License for more details.
00014 
00015    You should have received a copy of the GNU Library General Public License
00016    along with this library; see the file COPYING.LIB.  If not, write to
00017    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018    Boston, MA 02110-1301, USA.
00019 */
00020 
00021 // Local
00022 #include "helper.h"
00023 
00024 #include <kdebug.h>
00025 
00026 #include <QDate>
00027 
00028 #include <math.h>
00029 
00030 /*  DISABLED - we use KCalendarSystem instead
00031 void addMonths( QDate & date, int months )
00032 {
00033   int d = date.day();
00034   int m = date.month() + months;
00035   int y = date.year();
00036 
00037   if ( m > 12 )
00038   {
00039     y += (int) ( m / 12 );
00040     m %= 12;
00041   }
00042 
00043   // e.g. 31 Feb: decrease day...
00044   while ( !QDate::isValid( y, m, d ) && d > 0 )
00045     --d;
00046 
00047   date.setYMD( y, m, d );
00048 }
00049 
00050 void subMonths( QDate & date, int months )
00051 {
00052   int d = date.day();
00053   int m = date.month() - months;
00054   int y = date.year();
00055 
00056   while ( m < 1 )
00057   {
00058     m += 12;
00059     y -= 1;
00060   }
00061 
00062   // e.g. 31 Feb: decrease day
00063   while ( !QDate::isValid( y, m, d ) && d > 0 )
00064     --d;
00065 
00066   date.setYMD( y, m, d );
00067 }
00068 
00069 */
00070 
00071 int KSpread::daysPerYear(QDate const & date, int basis)
00072 {
00073   switch( basis )
00074   {
00075    case 0:
00076     return 360;
00077 
00078    case 1:
00079     if ( QDate::isLeapYear( date.year() ) )
00080       return 366;
00081     return 365;
00082 
00083    case 2:
00084     return 360;
00085    case 3:
00086     return 365;
00087    case 4:
00088     return 360;
00089   }
00090 
00091   return -1;
00092 }
00093 
00094 int KSpread::daysBetweenDates(QDate const & date1, QDate const & date2, int basis)
00095 {
00096   int day1, day2, month1, month2, year1, year2;
00097   bool isLeapYear = false;
00098   int days, months, years;
00099 
00100   day1   = date1.day();
00101   month1 = date1.month();
00102   year1  = date1.year();
00103   day2   = date2.day();
00104   month2 = date2.month();
00105   year2  = date2.year();
00106 
00107   years  = year2  - year1;
00108   months = month2 - month1 + years * 12;
00109   days   = day2   - day1;
00110 
00111   isLeapYear = QDate::isLeapYear( year1 );
00112 
00113   switch (basis)
00114   {
00115    case 0:
00116     if ( month1 == 2 && month2 != 2 && year1 == year2 )
00117     {
00118       if ( isLeapYear )
00119         return months * 30 + days - 1;
00120       else
00121         return months * 30 + days - 2;
00122     }
00123     return months * 30 + days;
00124 
00125    case 1: // TODO: real days for difference between months!
00126     //    return ( month2 - month1 ) * 30 + years * 360 + days;
00127 
00128    case 2: // TODO: real days for difference between months!
00129     //    return ( month2 - month1 ) * 30 + years * 365 + days;
00130 
00131    case 3:
00132     return date1.daysTo( date2 );
00133 
00134    case 4:
00135     return months * 30 + days;
00136   }
00137 
00138   return -1;
00139 }
00140 
00141 int KSpread::days360( int day1, int month1, int year1, bool leapYear1,
00142              int day2, int month2, int year2,
00143              bool usaMethod )
00144 {
00145   if ( day1 == 31 )
00146     day1--;
00147   else if ( usaMethod && ( month1 == 2 && ( day1 == 29 || ( day1 == 28 && ! leapYear1 ) ) ) )
00148     day1 = 30;
00149 
00150   if ( day2 == 31)
00151   {
00152     if ( usaMethod && day1 != 30 )
00153     {
00154        // date2 += 1
00155        day2 = 1;
00156        if ( month2 == 12 )
00157        {
00158          year2++;
00159          month2 = 1;
00160        }
00161        else
00162          month2++;
00163     }
00164     else
00165       day2 = 30;
00166   }
00167   return day2 + month2 * 30 + year2 * 360 - day1 - month1 * 30 - year1 * 360;
00168 }
00169 
00170 
00171 // days360
00172 int KSpread::days360( const QDate& _date1, const QDate& _date2, bool european )
00173 {
00174   int day1, month1, year1, day2, month2, year2;
00175   
00176   day1=_date1.day();
00177   month1=_date1.month();
00178   year1=_date1.year();
00179 
00180   day2=_date2.day();
00181   month2=_date2.month();
00182   year2=_date2.year();
00183  
00184   return days360(day1,month1,year1, QDate::isLeapYear(_date1.year()), day2,month2,year2, !european);
00185 }
00186 
00187 
00188 
00189 
00190 // // days360
00191 // int KSpread::days360( const QDate& _date1, const QDate& _date2, bool european )
00192 // {
00193 //   int day1, day2;
00194 //   int month1, month2;
00195 //   int year1, year2;
00196 //   bool negative = false;
00197 //   QDate date1( _date1 );
00198 //   QDate date2( _date2 );
00199 // 
00200 //   if (date1.daysTo( date2 ) < 0)
00201 //   {
00202 //     QDate tmp( date1 );
00203 //     date1 = date2;
00204 //     date2 = tmp;
00205 //     negative = true;
00206 //   }
00207 // 
00208 //   day1   = date1.day();
00209 //   day2   = date2.day();
00210 //   month1 = date1.month();
00211 //   month2 = date2.month();
00212 //   year1  = date1.year();
00213 //   year2  = date2.year();
00214 // 
00215 //   if ( european )
00216 //   {
00217 //     if ( day1 == 31 )
00218 //       day1 = 30;
00219 //     if ( day2 == 31 )
00220 //       day2 = 30;
00221 //   }
00222 //   else
00223 //   {
00224 //     // thanks to the Gnumeric developers for this...
00225 //     if ( month1 == 2 && month2 == 2
00226 //          && date1.daysInMonth() == day1
00227 //          && date2.daysInMonth() == day2 )
00228 //       day2 = 30;
00229 // 
00230 //     if ( month1 == 2 && date1.daysInMonth() == day1 )
00231 //       day1 = 30;
00232 // 
00233 //     if ( day2 == 31 && day1 >= 30 )
00234 //       day2 = 30;
00235 // 
00236 //     if ( day1 == 31 )
00237 //       day1 = 30;
00238 //   }
00239 // 
00240 //   return ( ( year2 - year1 ) * 12 + ( month2 - month1 ) ) * 30
00241 //     + ( day2 - day1 );
00242 // }
00243 
00244 // yearFrac
00245 long double KSpread::yearFrac( const QDate& refDate, const QDate& startDate, const QDate& endDate, int basis )
00246 {
00247   QDate date1 = startDate;
00248   QDate date2 = endDate;
00249 
00250   //
00251   // calculation
00252   //
00253 
00254   QDate date0=refDate; // referenceDate
00255 
00256   if ( date2 < date1 )
00257   {
00258     // exchange dates
00259     QDate Temp1=date1;
00260     date1=date2;
00261     date2=Temp1;
00262   }
00263 
00264   int days = date0.daysTo(date2) - date0.daysTo(date1);
00265 
00266 //   kDebug(36002) <<"date1 =" << date1 <<"    date2 =" << date2 <<"    days =" << days <<"    basis =" << basis;
00267 
00268   long double res=0;
00269   long double peryear=0;
00270   int nYears=0;
00271 
00272   switch(basis)
00273   {
00274     case 1:
00275     {
00276       nYears = date2.year() - date1.year();
00277       peryear = date1.daysInYear();
00278       
00279       if ( nYears && ( date1.month() > date2.month() || ( date1.month() == date2.month() && date1.day() > date2.day() ) ) )
00280         nYears--;
00281       
00282       if ( nYears )
00283       {
00284 //         kDebug(36002)<<" tmp("<<date2.year()<<","<<date1.month()<<","<<date1.day();
00285         QDate tmp(date2.year(), date1.month(), 1);
00286         tmp.addDays(date1.day()-1);
00287 //         kDebug(36002)<<" jul1 ="<<date2.toJulianDay()<<"  - jul2 ="<<tmp.toJulianDay();
00288         days = date2.toJulianDay() - tmp.toJulianDay();
00289 //         kDebug(36002)<<" days ="<<date2.daysTo(tmp);      
00290       }
00291       else
00292         days = date2.toJulianDay() - date1.toJulianDay();
00293       if ( days < 0 )
00294         days += peryear;
00295 
00296 //       kDebug(36002) <<"nYears =" << nYears <<"    peryear =" << peryear <<"    days =" << days;
00297       break;
00298 
00299       // old code
00300 //       int leaps=0,years=0;
00301 //       double k;
00302 // 
00303 //       if (days < (365 + QDate::isLeapYear(date1.year()) + 1))
00304 //       {
00305 //         // less than 1 year
00306 //         kDebug(36002) <<"less than 1 year ...";
00307 // 
00308 //         // bool 1 = 29.2. is in between dates
00309 //         k = (QDate::isLeapYear(date1.year()) && date1.month()<3) || (QDate::isLeapYear(date2.year()) && date2.month()*100+date2.day() >= 2*100+29);
00310 //         years = 1;
00311 //       }
00312 //       else
00313 //       {
00314 //         // more than 1 year
00315 //         kDebug(36002) <<"more than 1 year ...";
00316 //         years = date2.year()-date1.year()+1;
00317 //         leaps = QDate(date2.year()+1, 1, 1).toJulianDay() - QDate(date1.year(), 1, 1).toJulianDay() - 365*years;
00318 //         k = (double)leaps/years;
00319 //       }
00320 // 
00321 //       kDebug(36002) <<"leaps =" << leaps <<"    years =" << years <<"    leaps per year =" << (double)leaps/years;
00322 //       peryear = 365 + k;
00323 
00324 
00325     }
00326     case 2:
00327     {
00328       // Actual/360
00329       peryear = 360;
00330       break;
00331     }
00332     case 3:
00333     {
00334       // Actual/365
00335       peryear = 365;
00336       break;
00337     }
00338     case 4:
00339     {
00340       // 30/360 Europe
00341 
00342       // calc datedif360 (start, end, Europe)
00343       days = days360( date1, date2, 1);
00344 
00345       peryear = 360;
00346       break;
00347     }
00348     default:
00349     {
00350       // NASD 30/360
00351       //basis = 0;
00352 
00353       // calc datedif360 (start, end, US)
00354       days = days360( date1, date2, 0);
00355 
00356       peryear = 360;
00357     }
00358   }
00359 
00360   res = (long double)(nYears) + (long double)days / peryear;
00361 //   kDebug(36002)<<"getYearFrac res="<<res;
00362   return res;
00363 }
00364 
00365 // pow1p calculate (1+x)^y accurately
00366 long double KSpread::pow1p ( const long double& x, const long double& y)
00367 {
00368   if (fabs(x) > 0.5)
00369     return pow(1 + x, y);
00370   else
00371     return exp(y * log1p (x));
00372 }
00373 
00374 // pow1pm1 calculate ((1+x)^y)-1 accurately
00375 long double KSpread::pow1pm1 ( const long double& x, const long double& y)
00376 {
00377   if (x <= -1)
00378     return pow(1 + x, y) - 1;
00379   else
00380     return expm1(y * log1p (x));
00381 }
00382 
00383 long double KSpread::duration( const QDate& refDate, const QDate& settlement, const QDate& maturity, 
00384 const long double& coup_, const long double& yield_, const int& freq, const int& basis, const long double& numOfCoups)
00385 {
00386   long double yield = yield_;
00387   long double coup = coup_;
00388 
00389 //   kDebug(36002)<<"DURATION_HELPER";
00390 //   kDebug(36002)<<"sett ="<<settlement<<" mat ="<<maturity<<" coup ="<<coup<<" yield ="<<yield<<" freq ="<<freq<<" basis ="<<basis;
00391 
00392 
00393   long double yearfrac = yearFrac( refDate, settlement, maturity, basis);
00394   long double res = 0.0l;
00395   const long double f100 = 100.0l;
00396   coup *= f100 / (long double) ( freq );
00397   
00398   yield /= freq;
00399   yield += 1.0;
00400 
00401   long double diff = yearfrac * freq - numOfCoups;  
00402   
00403   long double t;
00404   
00405   for( t = 1.0l ; t < numOfCoups ; t += 1.0l )
00406     res += ( t + diff ) * ( coup ) / pow ( yield, t + diff );
00407 
00408   res += ( numOfCoups + diff ) * ( coup + f100 ) / pow( yield, numOfCoups + diff );
00409 
00410   long double p = 0.0l;
00411   for( t = 1.0l ; t < numOfCoups ; t += 1.0l )
00412     p += coup / pow( yield, t + diff );
00413 
00414   p += ( coup + f100 ) / pow( yield, numOfCoups + diff );
00415 
00416   res /= p;
00417   res /= (long double)( freq );
00418 
00419   return( res );
00420 }