|
|
|
/* This file is part of the KDE project
|
|
|
|
Copyright (C) 1998-2002 The KSpread Team
|
|
|
|
www.koffice.org/kspread
|
|
|
|
Copyright (C) 2005 Tomas Mecir <mecirt@gmail.com>
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
|
|
|
modify it under the terms of the GNU Library General Public
|
|
|
|
License as published by the Free Software Foundation; either
|
|
|
|
version 2 of the License.
|
|
|
|
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public License
|
|
|
|
along with this library; see the file COPYING.LIB. If not, write to
|
|
|
|
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
|
|
* Boston, MA 02110-1301, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// built-in financial functions
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
#include "functions.h"
|
|
|
|
#include "kspread_functions_helper.h"
|
|
|
|
#include "valuecalc.h"
|
|
|
|
#include "valueconverter.h"
|
|
|
|
|
|
|
|
#include <tdelocale.h>
|
|
|
|
#include <kcalendarsystem.h>
|
|
|
|
|
|
|
|
using namespace KSpread;
|
|
|
|
|
|
|
|
// prototypes (sorted)
|
|
|
|
Value func_accrint (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_accrintm (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_compound (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_continuous (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_coupnum (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_db (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_ddb (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_disc (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_dollarde (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_dollarfr (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_duration (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_effective (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_euro (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_fv (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_fv_annuity (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_intrate (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_ipmt (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_ispmt (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_level_coupon (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_nominal (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_nper (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_pmt (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_ppmt (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_pv (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_pv_annuity (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_received (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_sln (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_syd (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_tbilleq (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_tbillprice (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_tbillyield (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
Value func_zero_coupon (valVector args, ValueCalc *calc, FuncExtra *);
|
|
|
|
|
|
|
|
// registers all financial functions
|
|
|
|
void RegisterFinancialFunctions()
|
|
|
|
{
|
|
|
|
FunctionRepository* repo = FunctionRepository::self();
|
|
|
|
Function *f;
|
|
|
|
|
|
|
|
f = new Function ("ACCRINT", func_accrint);
|
|
|
|
f->setParamCount (6, 7);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("ACCRINTM", func_accrintm);
|
|
|
|
f->setParamCount (3, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("COMPOUND", func_compound);
|
|
|
|
f->setParamCount (4);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("CONTINUOUS", func_continuous);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("COUPNUM", func_coupnum);
|
|
|
|
f->setParamCount (3, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DB", func_db);
|
|
|
|
f->setParamCount (4, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DDB", func_ddb);
|
|
|
|
f->setParamCount (4, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DISC", func_disc);
|
|
|
|
f->setParamCount (4, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DOLLARDE", func_dollarde);
|
|
|
|
f->setParamCount (2);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DOLLARFR", func_dollarfr);
|
|
|
|
f->setParamCount (2);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("DURATION", func_duration);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("EFFECT", func_effective);
|
|
|
|
f->setParamCount (2);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("EFFECTIVE", func_effective);
|
|
|
|
f->setParamCount (2);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("EURO", func_euro); // KSpread-specific, Gnumeric-compatible
|
|
|
|
f->setParamCount (1);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("FV", func_fv);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("FV_ANNUITY", func_fv_annuity);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("INTRATE", func_intrate);
|
|
|
|
f->setParamCount (4, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("IPMT", func_ipmt);
|
|
|
|
f->setParamCount (4, 6);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("ISPMT", func_ispmt);
|
|
|
|
f->setParamCount (4);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("LEVEL_COUPON", func_level_coupon);
|
|
|
|
f->setParamCount (5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("NOMINAL", func_nominal);
|
|
|
|
f->setParamCount (2);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("NPER", func_nper);
|
|
|
|
f->setParamCount (3, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("PMT", func_pmt);
|
|
|
|
f->setParamCount (3, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("PPMT", func_ppmt);
|
|
|
|
f->setParamCount (4, 6);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("PV", func_pv);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("PV_ANNUITY", func_pv_annuity);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("RECEIVED", func_received);
|
|
|
|
f->setParamCount (4, 5);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("SLN", func_sln);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("SYD", func_syd);
|
|
|
|
f->setParamCount (4);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("TBILLEQ", func_tbilleq);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("TBILLPRICE", func_tbillprice);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("TBILLYIELD", func_tbillyield);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
f = new Function ("ZERO_COUPON", func_zero_coupon);
|
|
|
|
f->setParamCount (3);
|
|
|
|
repo->add (f);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Value getPay (ValueCalc *calc, Value rate,
|
|
|
|
Value nper, Value pv, Value fv, Value type)
|
|
|
|
{
|
|
|
|
Value pvif, fvifa;
|
|
|
|
|
|
|
|
if (calc->isZero (rate)) return Value::errorVALUE();
|
|
|
|
|
|
|
|
//pvif = pow( 1 + rate, nper );
|
|
|
|
//fvifa = ( pvif - 1 ) / rate;
|
|
|
|
pvif = calc->pow (calc->add (rate, 1), nper);
|
|
|
|
fvifa = calc->div (calc->sub (pvif, 1), rate);
|
|
|
|
|
|
|
|
// ( -pv * pvif - fv ) / ( ( 1.0 + rate * type ) * fvifa );
|
|
|
|
Value val1 = calc->sub (calc->mul (calc->mul (-1, pv), pvif), fv);
|
|
|
|
Value val2 = calc->mul (calc->add (1.0, calc->mul (rate, type)),
|
|
|
|
fvifa);
|
|
|
|
return calc->div (val1, val2);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Value getPrinc (ValueCalc *calc, Value start,
|
|
|
|
Value pay, Value rate, Value period)
|
|
|
|
{
|
|
|
|
// val1 = pow( 1 + rate, period )
|
|
|
|
Value val1 = calc->pow (calc->add (rate, 1), period);
|
|
|
|
// val2 = start * val1
|
|
|
|
Value val2 = calc->mul (start, val1);
|
|
|
|
// val3 = pay * ( ( val1 - 1 ) / rate )
|
|
|
|
Value val3 = calc->mul (pay, calc->div (calc->sub (val1, 1), rate));
|
|
|
|
// result = val2 + val3
|
|
|
|
return calc->add (val2, val3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: COUPNUM - taken from GNUMERIC
|
|
|
|
Value func_coupnum (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// dates and integers only - don't need high-precision for this
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
int frequency = calc->conv()->asInteger (args[2]).asInteger();
|
|
|
|
int basis = 0;
|
|
|
|
bool eom = true;
|
|
|
|
if (args.count() > 3)
|
|
|
|
basis = calc->conv()->asInteger (args[3]).asInteger();
|
|
|
|
if (args.count() == 5)
|
|
|
|
eom = calc->conv()->asBoolean (args[4]).asBoolean();
|
|
|
|
|
|
|
|
if (basis < 0 || basis > 5 || ( frequency == 0 ) || ( 12 % frequency != 0 )
|
|
|
|
|| settlement.daysTo( maturity ) <= 0)
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
double result;
|
|
|
|
TQDate cDate( maturity );
|
|
|
|
|
|
|
|
int months = maturity.month() - settlement.month()
|
|
|
|
+ 12 * ( maturity.year() - settlement.year() );
|
|
|
|
|
|
|
|
cDate = calc->conv()->locale()->calendar()->addMonths (cDate, -months);
|
|
|
|
|
|
|
|
if ( eom && maturity.daysInMonth() == maturity.day() )
|
|
|
|
{
|
|
|
|
while( cDate.daysInMonth() != cDate.day() )
|
|
|
|
cDate.addDays( 1 );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( settlement.day() >= cDate.day() )
|
|
|
|
--months;
|
|
|
|
|
|
|
|
result = ( 1 + months / ( 12 / frequency ) );
|
|
|
|
|
|
|
|
return Value (result);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: ACCRINT
|
|
|
|
Value func_accrint (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate firstInterest = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[2]).asDate();
|
|
|
|
|
|
|
|
Value rate = args[3];
|
|
|
|
Value par = args[4];
|
|
|
|
int frequency = calc->conv()->asInteger (args[5]).asInteger();
|
|
|
|
|
|
|
|
int basis = 0;
|
|
|
|
if (args.count() == 7)
|
|
|
|
basis = calc->conv()->asInteger (args[6]).asInteger();
|
|
|
|
|
|
|
|
if ( basis < 0 || basis > 4 || (calc->isZero (frequency)) ||
|
|
|
|
(12 % frequency != 0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
if ( ( settlement.daysTo( firstInterest ) < 0 )
|
|
|
|
|| ( firstInterest.daysTo( maturity ) > 0 ) )
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
double d = daysBetweenDates (maturity, settlement, basis);
|
|
|
|
double y = daysPerYear (maturity, basis);
|
|
|
|
|
|
|
|
if ( d < 0 || y <= 0 || calc->lower (par, 0) || calc->lower (rate, 0) ||
|
|
|
|
calc->isZero (rate))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
Value coeff = calc->div (calc->mul (par, rate), frequency);
|
|
|
|
double n = d / y;
|
|
|
|
|
|
|
|
return calc->mul (coeff, n * frequency);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: ACCRINTM
|
|
|
|
Value func_accrintm (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate issue = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
Value rate = args[2];
|
|
|
|
|
|
|
|
Value par = 1000;
|
|
|
|
int basis = 0;
|
|
|
|
if (args.count() > 3)
|
|
|
|
par = args[3];
|
|
|
|
if (args.count() == 5)
|
|
|
|
basis = calc->conv()->asInteger (args[4]).asInteger ();
|
|
|
|
|
|
|
|
double d = daysBetweenDates (issue, maturity, basis);
|
|
|
|
double y = daysPerYear (issue, basis);
|
|
|
|
|
|
|
|
if (d < 0 || y <= 0 || calc->isZero (par) || calc->isZero (rate) ||
|
|
|
|
calc->lower (par, 0) || calc->lower (rate, 0) || basis < 0 || basis > 4)
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// par*date * d/y
|
|
|
|
return calc->mul (calc->mul (par, rate), d / y);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: DISC
|
|
|
|
Value func_disc (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value par = args[2];
|
|
|
|
Value redemp = args[3];
|
|
|
|
|
|
|
|
int basis = 0;
|
|
|
|
if (args.count() == 5)
|
|
|
|
basis = calc->conv()->asInteger (args[4]).asInteger();
|
|
|
|
|
|
|
|
double y = daysPerYear (settlement, basis);
|
|
|
|
double d = daysBetweenDates (settlement, maturity, basis);
|
|
|
|
|
|
|
|
if ( y <= 0 || d <= 0 || basis < 0 || basis > 4 || calc->isZero (redemp) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// (redemp - par) / redemp * (y / d)
|
|
|
|
return calc->mul (calc->div (calc->sub (redemp, par), redemp), y / d);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Function: TBILLPRICE
|
|
|
|
Value func_tbillprice (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value discount = args[2];
|
|
|
|
|
|
|
|
double days = settlement.daysTo( maturity );
|
|
|
|
|
|
|
|
if (settlement > maturity || calc->lower (discount, 0) || days > 265)
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// (discount * days) / 360.0
|
|
|
|
Value val = calc->div (calc->mul (discount, days), 360.0);
|
|
|
|
// 100 * (1.0 - val);
|
|
|
|
return calc->mul (calc->sub (1.0, val), 100);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: TBILLYIELD
|
|
|
|
Value func_tbillyield (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value rate = args[2];
|
|
|
|
|
|
|
|
double days = settlement.daysTo( maturity );
|
|
|
|
|
|
|
|
if (settlement > maturity || calc->isZero (rate) || calc->lower (rate, 0)
|
|
|
|
|| days > 265)
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// (100.0 - rate) / rate * (360.0 / days);
|
|
|
|
return calc->mul (calc->div (calc->sub (100.0, rate), rate), 360.0 / days);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: TBILLEQ
|
|
|
|
Value func_tbilleq (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value discount = args[2];
|
|
|
|
|
|
|
|
double days = settlement.daysTo( maturity );
|
|
|
|
|
|
|
|
if (settlement > maturity || calc->lower (discount, 0) || days > 265)
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// 360 - discount*days
|
|
|
|
Value divisor = calc->sub (360.0, calc->mul (discount, days));
|
|
|
|
if (calc->isZero (divisor))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// 365.0 * discount / divisor
|
|
|
|
return calc->mul (calc->div (discount, divisor), 356.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: RECEIVED
|
|
|
|
Value func_received (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value investment = args[2];
|
|
|
|
Value discount = args[3];
|
|
|
|
|
|
|
|
int basis = 0;
|
|
|
|
if (args.count() == 5)
|
|
|
|
basis = calc->conv()->asInteger (args[4]).asInteger();
|
|
|
|
|
|
|
|
double d = daysBetweenDates( settlement, maturity, basis );
|
|
|
|
double y = daysPerYear( settlement, basis );
|
|
|
|
|
|
|
|
if ( d <= 0 || y <= 0 || basis < 0 || basis > 4 )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// 1.0 - ( discount * d / y )
|
|
|
|
Value x = calc->sub (1.0, (calc->mul (discount, d / y)));
|
|
|
|
|
|
|
|
if (calc->isZero (x))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
return calc->div (investment, x);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: DOLLARDE
|
|
|
|
Value func_dollarde (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value d = args[0];
|
|
|
|
Value f = args[1];
|
|
|
|
|
|
|
|
if (!calc->greater (f, 0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
Value tmp = f;
|
|
|
|
int n = 0;
|
|
|
|
while (calc->greater (tmp, 0))
|
|
|
|
{
|
|
|
|
tmp = calc->div (tmp, 10);
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
|
|
|
Value fl = calc->roundDown (d);
|
|
|
|
Value r = calc->sub (d, fl);
|
|
|
|
|
|
|
|
// fl + (r * pow(10.0, n) / f)
|
|
|
|
return calc->add (fl, calc->div (calc->mul (r, pow (10.0, n)), f));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: DOLLARFR
|
|
|
|
Value func_dollarfr (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value d = args[0];
|
|
|
|
Value f = args[1];
|
|
|
|
|
|
|
|
if (!calc->greater (f, 0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
Value tmp = f;
|
|
|
|
int n = 0;
|
|
|
|
while (calc->greater (tmp, 0))
|
|
|
|
{
|
|
|
|
tmp = calc->div (tmp, 10);
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
|
|
|
Value fl = calc->roundDown (d);
|
|
|
|
Value r = calc->sub (d, fl);
|
|
|
|
|
|
|
|
// fl + ((r * f) / pow (10.0, n));
|
|
|
|
return calc->add (fl, calc->div (calc->mul (r, f), pow (10.0, n)));
|
|
|
|
}
|
|
|
|
|
|
|
|
/// *** TODO continue here ***
|
|
|
|
|
|
|
|
// Function: INTRATE
|
|
|
|
Value func_intrate (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQDate settlement = calc->conv()->asDate (args[0]).asDate();
|
|
|
|
TQDate maturity = calc->conv()->asDate (args[1]).asDate();
|
|
|
|
|
|
|
|
Value invest = args[2];
|
|
|
|
Value redemption = args[3];
|
|
|
|
|
|
|
|
int basis = 0;
|
|
|
|
if (args.count() == 5)
|
|
|
|
basis = calc->conv()->asInteger (args[4]).asInteger();
|
|
|
|
|
|
|
|
double d = daysBetweenDates (settlement, maturity, basis);
|
|
|
|
double y = daysPerYear (settlement, basis);
|
|
|
|
|
|
|
|
if ( d <= 0 || y <= 0 || calc->isZero (invest) || basis < 0 || basis > 4 )
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// (redemption - invest) / invest * (y / d)
|
|
|
|
return calc->mul (calc->div (calc->sub (redemption, invest), invest), y/d);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Function: DURATION
|
|
|
|
Value func_duration (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value rate = args[0];
|
|
|
|
Value pv = args[1];
|
|
|
|
Value fv = args[2];
|
|
|
|
|
|
|
|
if (!calc->greater (rate, 0.0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
if (calc->isZero (fv) || calc->isZero (pv))
|
|
|
|
return Value::errorDIV0();
|
|
|
|
|
|
|
|
if (calc->lower (calc->div (fv, pv), 0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// log(fv / pv) / log(1.0 + rate)
|
|
|
|
return calc->div (calc->ln (calc->div (fv, pv)),
|
|
|
|
calc->ln (calc->add (rate, 1.0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: PMT
|
|
|
|
Value func_pmt (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value rate = args[0];
|
|
|
|
Value nper = args[1];
|
|
|
|
Value pv = args[2];
|
|
|
|
Value fv = 0.0;
|
|
|
|
Value type = 0;
|
|
|
|
if (args.count() > 3) fv = args[3];
|
|
|
|
if (args.count() == 5) type = args[4];
|
|
|
|
|
|
|
|
return getPay (calc, rate, nper, pv, fv, type);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: NPER
|
|
|
|
Value func_nper (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value rate = args[0];
|
|
|
|
Value pmt = args[1];
|
|
|
|
Value pv = args[2];
|
|
|
|
Value fv = 0.0;
|
|
|
|
Value type = 0;
|
|
|
|
if (args.count() > 3) fv = args[3];
|
|
|
|
if (args.count() == 5) type = args[4];
|
|
|
|
|
|
|
|
if (!calc->greater (rate, 0.0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// taken from Gnumeric
|
|
|
|
// v = 1.0 + rate * type
|
|
|
|
// d1 = pmt * v - fv * rate
|
|
|
|
// d2 = pmt * v - pv * rate
|
|
|
|
// res = d1 / d2;
|
|
|
|
Value v = calc->add (calc->mul (rate, type), 1.0);
|
|
|
|
Value d1 = calc->sub (calc->mul (pmt, v), calc->mul (fv, rate));
|
|
|
|
Value d2 = calc->add (calc->mul (pmt, v), calc->mul (pv, rate));
|
|
|
|
Value res = calc->div (d1, d2);
|
|
|
|
|
|
|
|
if (!calc->greater (res, 0.0)) // res must be >0
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// ln (res) / ln (rate + 1.0)
|
|
|
|
return calc->div (calc->ln (res), calc->ln (calc->add (rate, 1.0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: ISPMT
|
|
|
|
Value func_ispmt (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value rate = args[0];
|
|
|
|
Value per = args[1];
|
|
|
|
Value nper = args[2];
|
|
|
|
Value pv = args[3];
|
|
|
|
|
|
|
|
if (calc->lower (per, 1) || calc->greater (per, nper))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// d = -pv * rate
|
|
|
|
Value d = calc->mul (calc->mul (pv, -1), rate);
|
|
|
|
|
|
|
|
// d - (d / nper * per)
|
|
|
|
return calc->sub (d, calc->mul (calc->div (d, nper), per));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: IPMT
|
|
|
|
Value func_ipmt (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value rate = args[0];
|
|
|
|
Value per = args[1];
|
|
|
|
Value nper = args[2];
|
|
|
|
Value pv = args[3];
|
|
|
|
|
|
|
|
Value fv = 0.0;
|
|
|
|
Value type = 0;
|
|
|
|
if (args.count() > 4) fv = args[4];
|
|
|
|
if (args.count() == 6) type = args[5];
|
|
|
|
|
|
|
|
Value payment = getPay (calc, rate, nper, pv, fv, type);
|
|
|
|
Value ineg = getPrinc (calc, pv, payment, rate, calc->sub (per, 1));
|
|
|
|
|
|
|
|
// -ineg * rate
|
|
|
|
return calc->mul (calc->mul (ineg, -1), rate);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: PPMT
|
|
|
|
// Uses IPMT.
|
|
|
|
Value func_ppmt (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
Docs partly copied from OO.
|
|
|
|
Syntax
|
|
|
|
PPMT(Rate;Period;NPER;PV;FV;Type)
|
|
|
|
|
|
|
|
Rate is the periodic interest rate.
|
|
|
|
Period is the amortizement period. P=1 for the first and P=NPER for the last period.
|
|
|
|
NPER is the total number of periods during which annuity is paid.
|
|
|
|
PV is the present value in the sequence of payments.
|
|
|
|
FV (optional) is the desired (future) value.
|
|
|
|
Type (optional) defines the due date. F=1 for payment at the beginning of a period and F=0 for payment at the end of a period.
|
|
|
|
*/
|
|
|
|
|
|
|
|
Value rate = args[0];
|
|
|
|
Value per = args[1];
|
|
|
|
Value nper = args[2];
|
|
|
|
Value pv = args[3];
|
|
|
|
Value fv = 0.0;
|
|
|
|
Value type = 0;
|
|
|
|
if (args.count() > 4) fv = args[4];
|
|
|
|
if (args.count() == 6) type = args[5];
|
|
|
|
|
|
|
|
Value pay = getPay (calc, rate, nper, pv, fv, type);
|
|
|
|
Value ipmt = func_ipmt (args, calc, 0);
|
|
|
|
return calc->sub (pay, ipmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: FV
|
|
|
|
/* Returns future value, given current value, interest rate and time */
|
|
|
|
Value func_fv (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value present = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value periods = args[2];
|
|
|
|
|
|
|
|
// present * pow (1 + interest, periods)
|
|
|
|
return calc->mul (present, calc->pow (calc->add (interest, 1), periods));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: compound
|
|
|
|
/* Returns value after compounded interest, given principal, rate, periods
|
|
|
|
per year and year */
|
|
|
|
Value func_compound (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value principal = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value periods = args[2];
|
|
|
|
Value years = args[3];
|
|
|
|
|
|
|
|
// principal * pow(1+ (interest / periods), periods*years);
|
|
|
|
Value base = calc->add (calc->div (interest, periods), 1);
|
|
|
|
return calc->mul (principal, calc->pow (base, calc->mul (periods, years)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: continuous
|
|
|
|
/* Returns value after continuous compounding of interest, given principal,
|
|
|
|
rate and years */
|
|
|
|
Value func_continuous (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// If you still don't understand this, let me know! ;-) jsinger@leeta.net
|
|
|
|
Value principal = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value years = args[2];
|
|
|
|
|
|
|
|
// principal * exp(interest * years)
|
|
|
|
return calc->mul (principal, calc->exp (calc->mul (interest, years)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: PV
|
|
|
|
Value func_pv (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
/* Returns presnt value, given future value, interest rate and years */
|
|
|
|
Value future = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value periods = args[2];
|
|
|
|
|
|
|
|
// future / pow(1+interest, periods)
|
|
|
|
return calc->div (future, calc->pow (calc->add (interest, 1), periods));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: PV_annuity
|
|
|
|
Value func_pv_annuity (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value amount = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value periods = args[2];
|
|
|
|
|
|
|
|
// recpow = 1 / pow (1 + interest, periods)
|
|
|
|
// result = amount * (1 - recpow) / interest;
|
|
|
|
Value recpow;
|
|
|
|
recpow = calc->div (1, calc->pow (calc->add (interest, 1), periods));
|
|
|
|
return calc->mul (amount, calc->div (calc->sub (1, recpow), interest));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: FV_annnuity
|
|
|
|
Value func_fv_annuity (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
/* Returns future value of an annuity or cash flow, given payment, interest
|
|
|
|
rate and periods */
|
|
|
|
|
|
|
|
Value amount = args[0];
|
|
|
|
Value interest = args[1];
|
|
|
|
Value periods = args[2];
|
|
|
|
|
|
|
|
// pw = pow (1 + interest, periods)
|
|
|
|
// result = amount * ((pw - 1) / interest)
|
|
|
|
Value pw = calc->pow (calc->add (interest, 1), periods);
|
|
|
|
return calc->mul (amount, calc->div (calc->sub (pw, 1), interest));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: effective
|
|
|
|
Value func_effective (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// Returns effective interest rate given nominal rate and periods per year
|
|
|
|
|
|
|
|
Value nominal = args[0];
|
|
|
|
Value periods = args[1];
|
|
|
|
|
|
|
|
// base = 1 + (nominal / periods)
|
|
|
|
// result = pow (base, periods) - 1
|
|
|
|
Value base = calc->add (calc->div (nominal, periods), 1);
|
|
|
|
return calc->sub (calc->pow (base, periods), 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: zero_coupon
|
|
|
|
Value func_zero_coupon (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// Returns effective interest rate given nominal rate and periods per year
|
|
|
|
|
|
|
|
Value face = args[0];
|
|
|
|
Value rate = args[1];
|
|
|
|
Value years = args[2];
|
|
|
|
|
|
|
|
// face / pow(1 + rate, years)
|
|
|
|
return calc->div (face, calc->pow (calc->add (rate, 1), years));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: level_coupon
|
|
|
|
Value func_level_coupon (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// Returns effective interest rate given nominal rate and periods per year
|
|
|
|
Value face = args[0];
|
|
|
|
Value coupon_rate = args[1];
|
|
|
|
Value coupon_year = args[2];
|
|
|
|
Value years = args[3];
|
|
|
|
Value market_rate = args[4];
|
|
|
|
|
|
|
|
Value coupon, interest, pw, pv_annuity;
|
|
|
|
// coupon = coupon_rate * face / coupon_year
|
|
|
|
// interest = market_rate / coupon_year
|
|
|
|
// pw = pow(1 + interest, years * coupon_year)
|
|
|
|
// pv_annuity = (1 - 1 / pw) / interest
|
|
|
|
// result = coupon * pv_annuity + face / pw
|
|
|
|
coupon = calc->mul (coupon_rate, calc->div (face, coupon_year));
|
|
|
|
interest = calc->div (market_rate, coupon_year);
|
|
|
|
pw = calc->pow (calc->add (interest, 1), calc->mul (years, coupon_year));
|
|
|
|
pv_annuity = calc->div (calc->sub (1, calc->div (1, pw)), interest);
|
|
|
|
return calc->add (calc->mul (coupon, pv_annuity), calc->div (face, pw));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: nominal
|
|
|
|
Value func_nominal (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value effective = args[0];
|
|
|
|
Value periods = args[1];
|
|
|
|
|
|
|
|
if (calc->isZero (periods)) // Check null
|
|
|
|
return Value::errorDIV0();
|
|
|
|
|
|
|
|
// pw = pow (effective + 1, 1 / periods)
|
|
|
|
// result = periods * (pw - 1);
|
|
|
|
Value pw;
|
|
|
|
pw = calc->pow (calc->add (effective, 1), calc->div (1, periods));
|
|
|
|
return calc->mul (periods, calc->sub (pw, 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: SLN
|
|
|
|
/* straight-line depreciation for a single period */
|
|
|
|
Value func_sln (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value cost = args[0];
|
|
|
|
Value salvage_value = args[1];
|
|
|
|
Value life = args[2];
|
|
|
|
|
|
|
|
// sentinel check
|
|
|
|
if (!calc->greater (life, 0.0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// (cost - salvage_value) / life
|
|
|
|
return calc->div (calc->sub (cost, salvage_value), life);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: SYD
|
|
|
|
/* sum-of-years digits depreciation */
|
|
|
|
Value func_syd (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
Value cost = args[0];
|
|
|
|
Value salvage_value = args[1];
|
|
|
|
Value life = args[2];
|
|
|
|
Value period = args[3];
|
|
|
|
|
|
|
|
// sentinel check
|
|
|
|
if (!calc->greater (life, 0.0))
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
// v1 = cost - salvage_value
|
|
|
|
// v2 = life - period + 1
|
|
|
|
// v3 = life * (life + 1.0)
|
|
|
|
// result = (v1 * v2 * 2) / v3
|
|
|
|
Value v1, v2, v3;
|
|
|
|
v1 = calc->sub (cost, salvage_value);
|
|
|
|
v2 = calc->add (calc->sub (life, period), 1);
|
|
|
|
v3 = calc->mul (life, calc->add (life, 1.0));
|
|
|
|
return calc->div (calc->mul (calc->mul (v1, v2), 2), v3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: DB
|
|
|
|
/* fixed-declining depreciation */
|
|
|
|
Value func_db (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
// This function doesn't support extended datatypes, it simply
|
|
|
|
// converts everything to double - because it does quite a bit
|
|
|
|
// of computing, and, well, I'm lazy to convert it all (Tomas)
|
|
|
|
double cost = calc->conv()->asFloat (args[0]).asFloat();
|
|
|
|
double salvage = calc->conv()->asFloat (args[1]).asFloat();
|
|
|
|
double life = calc->conv()->asFloat (args[2]).asFloat();
|
|
|
|
double period = calc->conv()->asFloat (args[3]).asFloat();
|
|
|
|
double month = 12;
|
|
|
|
if (args.count() == 5)
|
|
|
|
month = calc->conv()->asFloat (args[4]).asFloat();
|
|
|
|
|
|
|
|
// sentinel check
|
|
|
|
if (cost == 0 || life <= 0.0)
|
|
|
|
return Value::errorVALUE ();
|
|
|
|
|
|
|
|
if (calc->lower (calc->div (salvage, cost), 0))
|
|
|
|
return Value::errorVALUE ();
|
|
|
|
|
|
|
|
double rate = 1000 * (1 - pow( (salvage/cost), (1/life) ));
|
|
|
|
rate = floor( rate + 0.5 ) / 1000;
|
|
|
|
|
|
|
|
double total = cost * rate * month / 12;
|
|
|
|
|
|
|
|
if( period == 1 )
|
|
|
|
return Value (total);
|
|
|
|
|
|
|
|
for (int i = 1; i < life; ++i)
|
|
|
|
if (i == period - 1)
|
|
|
|
return Value (rate * (cost-total));
|
|
|
|
else total += rate * (cost-total);
|
|
|
|
|
|
|
|
return Value ((cost-total) * rate * (12-month)/12);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: DDB
|
|
|
|
/* depreciation per period */
|
|
|
|
Value func_ddb (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
double cost = calc->conv()->asFloat (args[0]).asFloat();
|
|
|
|
double salvage = calc->conv()->asFloat (args[1]).asFloat();
|
|
|
|
double life = calc->conv()->asFloat (args[2]).asFloat();
|
|
|
|
double period = calc->conv()->asFloat (args[3]).asFloat();
|
|
|
|
double factor = 2;
|
|
|
|
if (args.count() == 5)
|
|
|
|
factor = calc->conv()->asFloat (args[4]).asFloat();
|
|
|
|
|
|
|
|
double total = 0.0;
|
|
|
|
|
|
|
|
if ( cost < 0.0 || salvage < 0.0 || life <= 0.0 || period < 0.0 || factor < 0.0 )
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
for( int i = 0; i < life-1; ++i )
|
|
|
|
{
|
|
|
|
double periodDep = ( cost - total ) * ( factor / life );
|
|
|
|
if ( i == period - 1 )
|
|
|
|
return Value (periodDep);
|
|
|
|
else
|
|
|
|
total += periodDep;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Value (cost - total - salvage);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Function: EURO
|
|
|
|
Value func_euro (valVector args, ValueCalc *calc, FuncExtra *)
|
|
|
|
{
|
|
|
|
TQString currency = calc->conv()->asString (args[0]).asString().upper();
|
|
|
|
double result = -1;
|
|
|
|
|
|
|
|
if( currency == "ATS" ) result = 13.7603; // Austria
|
|
|
|
else if( currency == "BEF" ) result = 40.3399; // Belgium
|
|
|
|
else if( currency == "DEM" ) result = 1.95583; // Germany
|
|
|
|
else if( currency == "ESP" ) result = 166.386; // Spain
|
|
|
|
else if( currency == "FIM" ) result = 5.94573; // Finland
|
|
|
|
else if( currency == "FRF" ) result = 6.55957; // France
|
|
|
|
else if( currency == "GRD" ) result = 340.75; // Greece
|
|
|
|
else if( currency == "IEP" ) result = 0.787564; // Ireland
|
|
|
|
else if( currency == "ITL" ) result = 1936.27; // Italy
|
|
|
|
else if( currency == "LUX" ) result = 40.3399; // Luxemburg
|
|
|
|
else if( currency == "NLG" ) result = 2.20371; // Nederland
|
|
|
|
else if( currency == "PTE" ) result = 200.482; // Portugal
|
|
|
|
else
|
|
|
|
return Value::errorVALUE();
|
|
|
|
|
|
|
|
return Value (result);
|
|
|
|
}
|