mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-29 13:49:10 +08:00
Financial functions: part 2 (#360)
* DB, DDB * DISC, DOLLARDE, DOLLARFR * EFFECT * FV * FVSCHEDULE, INTRATE, IPMT * IRR, ISPMT * MIRR, NOMINAL, NPER, NPV
This commit is contained in:
parent
64edd6c139
commit
dc34147325
@ -169,7 +169,7 @@ func (c Cell) SetNumber(v float64) {
|
|||||||
|
|
||||||
// cell type number
|
// cell type number
|
||||||
c.x.TAttr = sml.ST_CellTypeN
|
c.x.TAttr = sml.ST_CellTypeN
|
||||||
c.x.V = unioffice.String(strconv.FormatFloat(v, 'g', -1, 64))
|
c.x.V = unioffice.String(strconv.FormatFloat(v, 'f', -1, 64))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Column returns the cell column
|
// Column returns the cell column
|
||||||
|
@ -10,13 +10,11 @@ package formula
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
"math"
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterFunction("DURATION", Duration)
|
|
||||||
RegisterFunction("MDURATION", Mduration)
|
|
||||||
RegisterFunction("PDURATION", Pduration)
|
|
||||||
RegisterFunction("_xlfn.PDURATION", Pduration)
|
|
||||||
RegisterFunction("ACCRINTM", Accrintm)
|
RegisterFunction("ACCRINTM", Accrintm)
|
||||||
RegisterFunction("AMORDEGRC", Amordegrc)
|
RegisterFunction("AMORDEGRC", Amordegrc)
|
||||||
RegisterFunction("AMORLINC", Amorlinc)
|
RegisterFunction("AMORLINC", Amorlinc)
|
||||||
@ -28,6 +26,26 @@ func init() {
|
|||||||
RegisterFunction("COUPPCD", Couppcd)
|
RegisterFunction("COUPPCD", Couppcd)
|
||||||
RegisterFunction("CUMIPMT", Cumipmt)
|
RegisterFunction("CUMIPMT", Cumipmt)
|
||||||
RegisterFunction("CUMPRINC", Cumprinc)
|
RegisterFunction("CUMPRINC", Cumprinc)
|
||||||
|
RegisterFunction("DB", Db)
|
||||||
|
RegisterFunction("DDB", Ddb)
|
||||||
|
RegisterFunction("DISC", Disc)
|
||||||
|
RegisterFunction("DOLLARDE", Dollarde)
|
||||||
|
RegisterFunction("DOLLARFR", Dollarfr)
|
||||||
|
RegisterFunction("DURATION", Duration)
|
||||||
|
RegisterFunction("EFFECT", Effect)
|
||||||
|
RegisterFunction("FV", Fv)
|
||||||
|
RegisterFunction("FVSCHEDULE", Fvschedule)
|
||||||
|
RegisterFunction("INTRATE", Intrate)
|
||||||
|
RegisterFunction("IPMT", Ipmt)
|
||||||
|
RegisterFunction("IRR", Irr)
|
||||||
|
RegisterFunction("ISPMT", Ispmt)
|
||||||
|
RegisterFunction("MDURATION", Mduration)
|
||||||
|
RegisterFunction("MIRR", Mirr)
|
||||||
|
RegisterFunction("NOMINAL", Nominal)
|
||||||
|
RegisterFunction("NPER", Nper)
|
||||||
|
RegisterFunction("NPV", Npv)
|
||||||
|
RegisterFunction("PDURATION", Pduration)
|
||||||
|
RegisterFunction("_xlfn.PDURATION", Pduration)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Duration implements the Excel DURATION function.
|
// Duration implements the Excel DURATION function.
|
||||||
@ -789,3 +807,775 @@ func fv(rate, periods, payment, value float64, t int) float64 {
|
|||||||
}
|
}
|
||||||
return -result
|
return -result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Db implements the Excel DB function.
|
||||||
|
func Db(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum != 4 && argsNum != 5 {
|
||||||
|
return MakeErrorResult("DB requires four or five number arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DB requires cost to be number argument")
|
||||||
|
}
|
||||||
|
cost := args[0].ValueNumber
|
||||||
|
if cost < 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DB requires cost to be non negative")
|
||||||
|
}
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DB requires salvage to be number argument")
|
||||||
|
}
|
||||||
|
salvage := args[1].ValueNumber
|
||||||
|
if salvage < 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DB requires salvage to be non negative")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DB requires life to be number argument")
|
||||||
|
}
|
||||||
|
life := args[2].ValueNumber
|
||||||
|
if life <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DB requires life to be positive")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DB requires period to be number argument")
|
||||||
|
}
|
||||||
|
period := args[3].ValueNumber
|
||||||
|
if period <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DB requires period to be positive")
|
||||||
|
}
|
||||||
|
if period - life > 1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Incorrect period for DB")
|
||||||
|
}
|
||||||
|
month := 12.0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DB requires month to be number argument")
|
||||||
|
}
|
||||||
|
month = args[4].ValueNumber
|
||||||
|
if month < 1 || month > 12 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DB requires month to be in range of 1 and 12")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if month == 12 && period > life {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Incorrect period for DB")
|
||||||
|
}
|
||||||
|
if salvage >= cost {
|
||||||
|
return MakeNumberResult(0)
|
||||||
|
}
|
||||||
|
rate := 1 - math.Pow(salvage / cost, 1 / life)
|
||||||
|
rate = float64(int(rate * 1000 + 0.5)) / 1000 // round to 3 decimal places
|
||||||
|
initial := cost * rate * month / 12
|
||||||
|
if period == 1 {
|
||||||
|
return MakeNumberResult(initial)
|
||||||
|
}
|
||||||
|
total := initial
|
||||||
|
current := 0.0
|
||||||
|
ceiling := life
|
||||||
|
if ceiling > period {
|
||||||
|
ceiling = period
|
||||||
|
}
|
||||||
|
for i := 2.0; i <= ceiling; i++ {
|
||||||
|
current = (cost - total) * rate
|
||||||
|
total += current
|
||||||
|
}
|
||||||
|
if period > life {
|
||||||
|
return MakeNumberResult((cost - total) * rate * (12 - month) / 12)
|
||||||
|
}
|
||||||
|
return MakeNumberResult(current)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ddb implements the Excel DDB function.
|
||||||
|
func Ddb(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum != 4 && argsNum != 5 {
|
||||||
|
return MakeErrorResult("DDB requires four or five number arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DDB requires cost to be number argument")
|
||||||
|
}
|
||||||
|
cost := args[0].ValueNumber
|
||||||
|
if cost < 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DDB requires cost to be non negative")
|
||||||
|
}
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DDB requires salvage to be number argument")
|
||||||
|
}
|
||||||
|
salvage := args[1].ValueNumber
|
||||||
|
if salvage < 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DDB requires salvage to be non negative")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DDB requires life to be number argument")
|
||||||
|
}
|
||||||
|
life := args[2].ValueNumber
|
||||||
|
if life <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DDB requires life to be positive")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DDB requires period to be number argument")
|
||||||
|
}
|
||||||
|
period := args[3].ValueNumber
|
||||||
|
if period < 1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DDB requires period to be positive")
|
||||||
|
}
|
||||||
|
if period > life {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Incorrect period for DDB")
|
||||||
|
}
|
||||||
|
factor := 2.0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DDB requires factor to be number argument")
|
||||||
|
}
|
||||||
|
factor = args[4].ValueNumber
|
||||||
|
if factor < 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DDB requires factor to be non negative")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
oldValue := 0.0
|
||||||
|
rate := factor / life
|
||||||
|
if rate >= 1 {
|
||||||
|
rate = 1
|
||||||
|
if period == 1 {
|
||||||
|
oldValue = cost
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
oldValue = cost * math.Pow(1 - rate, period - 1)
|
||||||
|
}
|
||||||
|
newValue := cost * math.Pow(1 - rate, period)
|
||||||
|
|
||||||
|
var ddb float64
|
||||||
|
|
||||||
|
if newValue < salvage {
|
||||||
|
ddb = oldValue - salvage
|
||||||
|
} else {
|
||||||
|
ddb = oldValue - newValue
|
||||||
|
}
|
||||||
|
if ddb < 0 {
|
||||||
|
ddb = 0
|
||||||
|
}
|
||||||
|
return MakeNumberResult(ddb)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disc implements the Excel DISC function.
|
||||||
|
func Disc(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum != 4 && argsNum != 5 {
|
||||||
|
return MakeErrorResult("DISC requires four or five arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DISC requires settlement date to be number argument")
|
||||||
|
}
|
||||||
|
settlement := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DISC requires maturity date to be number argument")
|
||||||
|
}
|
||||||
|
maturity := args[1].ValueNumber
|
||||||
|
if settlement >= maturity {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DISC requires maturity date to be later than settlement date")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DISC requires pr to be number argument")
|
||||||
|
}
|
||||||
|
pr := args[2].ValueNumber
|
||||||
|
if pr <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DISC requires pr to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DISC requires redemption to be number argument")
|
||||||
|
}
|
||||||
|
redemption := args[3].ValueNumber
|
||||||
|
if redemption <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "DISC requires redemption to be positive number argument")
|
||||||
|
}
|
||||||
|
basis := 0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("DISC requires basis to be number argument")
|
||||||
|
}
|
||||||
|
basis = int(args[4].ValueNumber)
|
||||||
|
if !checkBasis(basis) {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for DISC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fracResult := yearFrac(settlement, maturity, basis)
|
||||||
|
if fracResult.Type == ResultTypeError {
|
||||||
|
return fracResult
|
||||||
|
}
|
||||||
|
return MakeNumberResult((redemption - pr) / redemption / fracResult.ValueNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dollarde implements the Excel DOLLARDE function.
|
||||||
|
func Dollarde(args []Result) Result {
|
||||||
|
dollar, fraction, resultErr := parseDollarArgs(args, "DOLLARDE")
|
||||||
|
if resultErr.Type == ResultTypeError {
|
||||||
|
return resultErr
|
||||||
|
}
|
||||||
|
if fraction < 1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeDivideByZero, "DOLLARDE requires fraction to be equal or more than 1")
|
||||||
|
}
|
||||||
|
if dollar == 0 {
|
||||||
|
return MakeNumberResult(0)
|
||||||
|
}
|
||||||
|
neg := dollar < 0
|
||||||
|
if neg {
|
||||||
|
dollar = -dollar
|
||||||
|
}
|
||||||
|
dollarStr := args[0].Value()
|
||||||
|
split := strings.Split(dollarStr, ".")
|
||||||
|
dollarInt := float64(int(dollar))
|
||||||
|
dollarFracStr := split[1]
|
||||||
|
dollarFracOrder := len(dollarFracStr)
|
||||||
|
fractionOrder := int(math.Log10(fraction)) + 1
|
||||||
|
power := float64(fractionOrder - dollarFracOrder)
|
||||||
|
dollarFrac, err := strconv.ParseFloat(dollarFracStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return MakeErrorResult("Incorrect fraction argument for DOLLARDE")
|
||||||
|
}
|
||||||
|
dollarFrac *= math.Pow(10, power)
|
||||||
|
dollarde := dollarInt + dollarFrac / fraction
|
||||||
|
if neg {
|
||||||
|
dollarde = -dollarde
|
||||||
|
}
|
||||||
|
return MakeNumberResult(dollarde)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dollarfr implements the Excel DOLLARFR function.
|
||||||
|
func Dollarfr(args []Result) Result {
|
||||||
|
dollar, fraction, resultErr := parseDollarArgs(args, "DOLLARFR")
|
||||||
|
if resultErr.Type == ResultTypeError {
|
||||||
|
return resultErr
|
||||||
|
}
|
||||||
|
if dollar == 0 {
|
||||||
|
return MakeNumberResult(0)
|
||||||
|
}
|
||||||
|
neg := dollar < 0
|
||||||
|
if neg {
|
||||||
|
dollar = -dollar
|
||||||
|
}
|
||||||
|
dollarInt := float64(int(dollar))
|
||||||
|
dollarStr := args[0].Value()
|
||||||
|
split := strings.Split(dollarStr, ".")
|
||||||
|
dollarFracStr := split[1]
|
||||||
|
dollarFrac, err := strconv.ParseFloat(dollarFracStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return MakeErrorResult("Incorrect fraction argument for DOLLARFR")
|
||||||
|
}
|
||||||
|
dollarFracOrder := float64(len(dollarFracStr))
|
||||||
|
dollarFrac /= math.Pow(10, dollarFracOrder)
|
||||||
|
|
||||||
|
dollarfr := dollarFrac * fraction / math.Pow(10, float64(int(math.Log10(fraction))) + 1) + dollarInt
|
||||||
|
if neg {
|
||||||
|
dollarfr = -dollarfr
|
||||||
|
}
|
||||||
|
return MakeNumberResult(dollarfr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseDollarArgs(args []Result, funcName string) (float64, float64, Result) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return 0, 0, MakeErrorResult(funcName + " requires two arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return 0, 0, MakeErrorResult(funcName + " requires fractional dollar to be number argument")
|
||||||
|
}
|
||||||
|
dollar := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return 0, 0, MakeErrorResult(funcName + " requires fraction to be number argument")
|
||||||
|
}
|
||||||
|
fraction := float64(int(args[1].ValueNumber))
|
||||||
|
if fraction < 0 {
|
||||||
|
return 0, 0, MakeErrorResultType(ErrorTypeNum, funcName + " requires fraction to be positive number")
|
||||||
|
}
|
||||||
|
return dollar, fraction, MakeEmptyResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect implements the Excel EFFECT function.
|
||||||
|
func Effect(args []Result) Result {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return MakeErrorResult("EFFECT requires two arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("EFFECT requires nominal interest rate to be number argument")
|
||||||
|
}
|
||||||
|
nominal := args[0].ValueNumber
|
||||||
|
if nominal <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "EFFECT requires nominal interest rate to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("EFFECT requires number of compounding periods to be number argument")
|
||||||
|
}
|
||||||
|
npery := float64(int(args[1].ValueNumber))
|
||||||
|
if npery < 1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "EFFECT requires number of compounding periods to be 1 or more")
|
||||||
|
}
|
||||||
|
return MakeNumberResult(math.Pow((1 + nominal / npery), npery) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fv implements the Excel FV function.
|
||||||
|
func Fv(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum < 3 || argsNum > 5 {
|
||||||
|
return MakeErrorResult("FV requires number of arguments in range of 3 and 5")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires rate to be number argument")
|
||||||
|
}
|
||||||
|
rate := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires number of periods to be number argument")
|
||||||
|
}
|
||||||
|
nPer := args[1].ValueNumber
|
||||||
|
if nPer != float64(int(nPer)) {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "FV requires number of periods to be integer number argument")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires payment to be number argument")
|
||||||
|
}
|
||||||
|
pmt := args[2].ValueNumber
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires payment to be number argument")
|
||||||
|
}
|
||||||
|
pv := 0.0
|
||||||
|
if argsNum >= 4 {
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires present value to be number argument")
|
||||||
|
}
|
||||||
|
pv = args[3].ValueNumber
|
||||||
|
}
|
||||||
|
t := 0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FV requires type to be number argument")
|
||||||
|
}
|
||||||
|
t = int(args[4].ValueNumber)
|
||||||
|
if t != 0 {
|
||||||
|
t = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MakeNumberResult(fv(rate, nPer, pmt, pv, t))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fvschedule implements the Excel FVSCHEDULE function.
|
||||||
|
func Fvschedule(args []Result) Result {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return MakeErrorResult("FVSCHEDULE requires two arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("FVSCHEDULE requires principal to be number argument")
|
||||||
|
}
|
||||||
|
principal := args[0].ValueNumber
|
||||||
|
switch args[1].Type {
|
||||||
|
case ResultTypeNumber:
|
||||||
|
return MakeNumberResult(principal * (args[1].ValueNumber+1))
|
||||||
|
case ResultTypeList, ResultTypeArray:
|
||||||
|
schedule := arrayFromRange(args[1])
|
||||||
|
for _, row := range schedule {
|
||||||
|
for _, rate := range row {
|
||||||
|
if rate.Type != ResultTypeNumber || rate.IsBoolean {
|
||||||
|
return MakeErrorResult("FVSCHEDULE requires rates to be numbers")
|
||||||
|
}
|
||||||
|
principal *= 1.0 + rate.ValueNumber
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return MakeNumberResult(principal)
|
||||||
|
default:
|
||||||
|
return MakeErrorResult("FVSCHEDULE requires schedule to be of array type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Intrate implements the Excel INTRATE function.
|
||||||
|
func Intrate(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum != 4 && argsNum != 5 {
|
||||||
|
return MakeErrorResult("INTRATE requires four or five arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("INTRATE requires settlement date to be number argument")
|
||||||
|
}
|
||||||
|
settlement := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("INTRATE requires maturity date to be number argument")
|
||||||
|
}
|
||||||
|
maturity := args[1].ValueNumber
|
||||||
|
if settlement >= maturity {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "INTRATE requires maturity date to be later than settlement date")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("INTRATE requires investment to be number argument")
|
||||||
|
}
|
||||||
|
investment := args[2].ValueNumber
|
||||||
|
if investment <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "INTRATE requires investment to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("INTRATE requires redemption to be number argument")
|
||||||
|
}
|
||||||
|
redemption := args[3].ValueNumber
|
||||||
|
if redemption <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "INTRATE requires redemption to be positive number argument")
|
||||||
|
}
|
||||||
|
basis := 0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("INTRATE requires basis to be number argument")
|
||||||
|
}
|
||||||
|
basis = int(args[4].ValueNumber)
|
||||||
|
if !checkBasis(basis) {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "Incorrect basis argument for INTRATE")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fracResult := yearFrac(settlement, maturity, basis)
|
||||||
|
if fracResult.Type == ResultTypeError {
|
||||||
|
return fracResult
|
||||||
|
}
|
||||||
|
return MakeNumberResult((redemption - investment) / investment / fracResult.ValueNumber)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ipmt implements the Excel IPMT function.
|
||||||
|
func Ipmt(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum < 4 || argsNum > 6 {
|
||||||
|
return MakeErrorResult("IPMT requires six arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires rate to be number argument")
|
||||||
|
}
|
||||||
|
rate := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires period to be number argument")
|
||||||
|
}
|
||||||
|
period := args[1].ValueNumber
|
||||||
|
if period <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "IPMT requires period to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires number of periods to be number argument")
|
||||||
|
}
|
||||||
|
nPer := args[2].ValueNumber
|
||||||
|
if nPer <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "IPMT requires number of periods to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires present value to be number argument")
|
||||||
|
}
|
||||||
|
presentValue := args[3].ValueNumber
|
||||||
|
futureValue := 0.0
|
||||||
|
if argsNum > 4 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires future value to be number argument")
|
||||||
|
}
|
||||||
|
futureValue = args[4].ValueNumber
|
||||||
|
}
|
||||||
|
t := 0
|
||||||
|
if argsNum == 6 {
|
||||||
|
if args[5].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IPMT requires start period to be number argument")
|
||||||
|
}
|
||||||
|
t = int(args[5].ValueNumber)
|
||||||
|
if t != 0 {
|
||||||
|
t = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payment := pmt(rate, nPer, presentValue, futureValue, t)
|
||||||
|
var interest float64
|
||||||
|
if period == 1 {
|
||||||
|
if t == 1 {
|
||||||
|
interest = 0
|
||||||
|
} else {
|
||||||
|
interest = -presentValue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if t == 1 {
|
||||||
|
interest = fv(rate, period - 2, payment, presentValue, 1) - payment
|
||||||
|
} else {
|
||||||
|
interest = fv(rate, period - 1, payment, presentValue, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return MakeNumberResult(interest * rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Irr implements the Excel IRR function.
|
||||||
|
func Irr(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum > 2 {
|
||||||
|
return MakeErrorResult("IRR requires one or two arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeList && args[0].Type != ResultTypeArray {
|
||||||
|
return MakeErrorResult("IRR requires values to be range argument")
|
||||||
|
}
|
||||||
|
valuesR := arrayFromRange(args[0])
|
||||||
|
values := []float64{}
|
||||||
|
for _, row := range valuesR {
|
||||||
|
for _, vR := range row {
|
||||||
|
if vR.Type == ResultTypeNumber && !vR.IsBoolean {
|
||||||
|
values = append(values, vR.ValueNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vlen := len(values)
|
||||||
|
if len(values) < 2 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "")
|
||||||
|
}
|
||||||
|
guess := 0.1
|
||||||
|
if argsNum == 2 {
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("IRR requires guess to be number argument")
|
||||||
|
}
|
||||||
|
guess = args[1].ValueNumber
|
||||||
|
if guess <= -1 {
|
||||||
|
return MakeErrorResult("IRR requires guess to be more than -1")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dates := []float64{}
|
||||||
|
positive := false
|
||||||
|
negative := false
|
||||||
|
|
||||||
|
for i := 0; i < vlen; i++ {
|
||||||
|
if i == 0 {
|
||||||
|
dates = append(dates, 0)
|
||||||
|
} else {
|
||||||
|
dates = append(dates, dates[i - 1] + 365)
|
||||||
|
}
|
||||||
|
if values[i] > 0 {
|
||||||
|
positive = true
|
||||||
|
}
|
||||||
|
if values[i] < 0 {
|
||||||
|
negative = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !positive || !negative {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
resultRate := guess
|
||||||
|
epsMax := 1e-10
|
||||||
|
iter := 0
|
||||||
|
maxIter := 50
|
||||||
|
isErr := false
|
||||||
|
|
||||||
|
for {
|
||||||
|
resultValue := irrResult(values, dates, resultRate)
|
||||||
|
newRate := resultRate - resultValue / irrResultDeriv(values, dates, resultRate)
|
||||||
|
epsRate := math.Abs(newRate - resultRate)
|
||||||
|
resultRate = newRate
|
||||||
|
iter++
|
||||||
|
if iter > maxIter || epsRate <= epsMax || math.Abs(resultValue) <= epsMax {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if iter > maxIter {
|
||||||
|
isErr = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isErr || math.IsNaN(resultRate) || math.IsInf(resultRate, 0) {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "")
|
||||||
|
}
|
||||||
|
return MakeNumberResult(resultRate)
|
||||||
|
}
|
||||||
|
|
||||||
|
func irrResult(values, dates []float64, rate float64) float64 {
|
||||||
|
r := rate + 1
|
||||||
|
result := values[0]
|
||||||
|
vlen := len(values)
|
||||||
|
firstDate := dates[0]
|
||||||
|
for i := 1; i < vlen; i++ {
|
||||||
|
result += values[i] / math.Pow(r, (dates[i] - firstDate) / 365)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func irrResultDeriv(values, dates []float64, rate float64) float64 {
|
||||||
|
r := rate + 1
|
||||||
|
result := 0.0
|
||||||
|
vlen := len(values)
|
||||||
|
firstDate := dates[0]
|
||||||
|
for i := 1; i < vlen; i++ {
|
||||||
|
frac := (dates[i] - firstDate) / 365
|
||||||
|
result -= frac * values[i] / math.Pow(r, frac + 1)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ispmt implements the Excel ISPMT function.
|
||||||
|
func Ispmt(args []Result) Result {
|
||||||
|
if len(args) != 4 {
|
||||||
|
return MakeErrorResult("ISPMT requires six arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("ISPMT requires rate to be number argument")
|
||||||
|
}
|
||||||
|
rate := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("ISPMT requires period to be number argument")
|
||||||
|
}
|
||||||
|
period := args[1].ValueNumber
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("ISPMT requires number of periods to be number argument")
|
||||||
|
}
|
||||||
|
nPer := args[2].ValueNumber
|
||||||
|
if nPer <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "ISPMT requires number of periods to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("ISPMT requires present value to be number argument")
|
||||||
|
}
|
||||||
|
pv := args[3].ValueNumber
|
||||||
|
|
||||||
|
return MakeNumberResult(pv * rate * (period / nPer - 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mirr implements the Excel MIRR function.
|
||||||
|
func Mirr(args []Result) Result {
|
||||||
|
if len(args) != 3 {
|
||||||
|
return MakeErrorResult("MIRR requires three arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeList && args[0].Type != ResultTypeArray {
|
||||||
|
return MakeErrorResult("MIRR requires values to be range argument")
|
||||||
|
}
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("MIRR requires finance rate to be number argument")
|
||||||
|
}
|
||||||
|
finRate := args[1].ValueNumber + 1
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("MIRR requires reinvest rate to be number argument")
|
||||||
|
}
|
||||||
|
reinvRate := args[2].ValueNumber + 1
|
||||||
|
if reinvRate == 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeDivideByZero, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
valuesR := arrayFromRange(args[0])
|
||||||
|
n := float64(len(valuesR))
|
||||||
|
npvInvest, npvReinvest := 0.0, 0.0
|
||||||
|
powInvest, powReinvest := 1.0, 1.0
|
||||||
|
hasPositive, hasNegative := false, false
|
||||||
|
for _, row := range valuesR {
|
||||||
|
for _, vR := range row {
|
||||||
|
if vR.Type == ResultTypeNumber && !vR.IsBoolean {
|
||||||
|
v := vR.ValueNumber
|
||||||
|
if v == 0 {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
if v > 0 {
|
||||||
|
hasPositive = true
|
||||||
|
npvReinvest += vR.ValueNumber * powReinvest
|
||||||
|
} else {
|
||||||
|
hasNegative = true
|
||||||
|
npvInvest += vR.ValueNumber * powInvest
|
||||||
|
}
|
||||||
|
powInvest /= finRate
|
||||||
|
powReinvest /= reinvRate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasPositive || !hasNegative {
|
||||||
|
return MakeErrorResultType(ErrorTypeDivideByZero, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
result := -npvReinvest / npvInvest
|
||||||
|
result *= math.Pow(reinvRate, n - 1)
|
||||||
|
result = math.Pow(result, 1 / (n - 1))
|
||||||
|
return MakeNumberResult(result - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nominal implements the Excel NOMINAL function.
|
||||||
|
func Nominal(args []Result) Result {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return MakeErrorResult("NOMINAL requires two arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NOMINAL requires nominal interest rate to be number argument")
|
||||||
|
}
|
||||||
|
effect := args[0].ValueNumber
|
||||||
|
if effect <= 0 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "NOMINAL requires effect interest rate to be positive number argument")
|
||||||
|
}
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NOMINAL requires number of compounding periods to be number argument")
|
||||||
|
}
|
||||||
|
npery := float64(int(args[1].ValueNumber))
|
||||||
|
if npery < 1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeNum, "NOMINAL requires number of compounding periods to be 1 or more")
|
||||||
|
}
|
||||||
|
return MakeNumberResult((math.Pow(effect + 1, 1 / npery) - 1) * npery)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nper implements the Excel NPER function.
|
||||||
|
func Nper(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum < 3 || argsNum > 5 {
|
||||||
|
return MakeErrorResult("NPER requires number of arguments in range of 3 and 5")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPER requires rate to be number argument")
|
||||||
|
}
|
||||||
|
rate := args[0].ValueNumber
|
||||||
|
if args[1].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPER requires payment to be number argument")
|
||||||
|
}
|
||||||
|
pmt := args[1].ValueNumber
|
||||||
|
if args[2].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPER requires present value to be number argument")
|
||||||
|
}
|
||||||
|
pv := args[2].ValueNumber
|
||||||
|
fv := 0.0
|
||||||
|
if argsNum >= 4 {
|
||||||
|
if args[3].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPER requires future value to be number argument")
|
||||||
|
}
|
||||||
|
fv = args[3].ValueNumber
|
||||||
|
}
|
||||||
|
t := 0.0
|
||||||
|
if argsNum == 5 {
|
||||||
|
if args[4].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPER requires type to be number argument")
|
||||||
|
}
|
||||||
|
t = args[4].ValueNumber
|
||||||
|
if t != 0 {
|
||||||
|
t = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
num := pmt * (1 + rate * t) - fv * rate
|
||||||
|
den := (pv * rate + pmt * (1 + rate * t))
|
||||||
|
return MakeNumberResult(math.Log(num / den) / math.Log(1 + rate))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Npv implements the Excel NPV function.
|
||||||
|
func Npv(args []Result) Result {
|
||||||
|
argsNum := len(args)
|
||||||
|
if argsNum < 2 {
|
||||||
|
return MakeErrorResult("NPV requires two or more arguments")
|
||||||
|
}
|
||||||
|
if args[0].Type != ResultTypeNumber {
|
||||||
|
return MakeErrorResult("NPV requires rate to be number argument")
|
||||||
|
}
|
||||||
|
rate := args[0].ValueNumber
|
||||||
|
if rate == -1 {
|
||||||
|
return MakeErrorResultType(ErrorTypeDivideByZero, "")
|
||||||
|
}
|
||||||
|
values := []float64{}
|
||||||
|
for _, arg := range args[1:] {
|
||||||
|
switch arg.Type {
|
||||||
|
case ResultTypeNumber:
|
||||||
|
values = append(values, arg.ValueNumber)
|
||||||
|
case ResultTypeArray, ResultTypeList:
|
||||||
|
rangeR := arrayFromRange(arg)
|
||||||
|
for _, r := range rangeR {
|
||||||
|
for _, vR := range r {
|
||||||
|
if vR.Type == ResultTypeNumber && !vR.IsBoolean {
|
||||||
|
values = append(values, vR.ValueNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
npv := 0.0
|
||||||
|
for i, value := range values {
|
||||||
|
npv += value / math.Pow(1 + rate, float64(i) + 1)
|
||||||
|
}
|
||||||
|
return MakeNumberResult(npv)
|
||||||
|
}
|
||||||
|
@ -1868,3 +1868,327 @@ func TestCumprinc(t *testing.T) {
|
|||||||
|
|
||||||
runTests(t, ctx, td)
|
runTests(t, ctx, td)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDb(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(1000000)
|
||||||
|
sheet.Cell("A2").SetNumber(100000)
|
||||||
|
sheet.Cell("A3").SetNumber(6)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=DB(A1,A2,A3,1,7)`, `186083.333333 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,2,7)`, `259639.416666 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,3,7)`, `176814.44275 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,4,7)`, `120410.635512 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,5,7)`, `81999.6427841 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,6,7)`, `55841.756736 ResultTypeNumber`},
|
||||||
|
{`=DB(A1,A2,A3,7,7)`, `15845.0984738 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDdb(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(2400)
|
||||||
|
sheet.Cell("A2").SetNumber(300)
|
||||||
|
sheet.Cell("A3").SetNumber(10)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=DDB(A1,A2,A3*365,1)`, `1.31506849315 ResultTypeNumber`},
|
||||||
|
{`=DDB(A1,A2,A3*12,1,2)`, `40 ResultTypeNumber`},
|
||||||
|
{`=DDB(A1,A2,A3,1,2)`, `480 ResultTypeNumber`},
|
||||||
|
{`=DDB(A1,A2,A3,2,1.5)`, `306. ResultTypeNumber`},
|
||||||
|
{`=DDB(A1,A2,A3,10)`, `22.1225472 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDisc(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetDate(time.Date(2018, 7, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
sheet.Cell("A2").SetDate(time.Date(2048, 1, 1, 0, 0, 0, 0, time.UTC))
|
||||||
|
sheet.Cell("A3").SetNumber(97.975)
|
||||||
|
sheet.Cell("A4").SetNumber(100)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=DISC(A1,A2,A3,A4,0)`, `0.00068644067 ResultTypeNumber`},
|
||||||
|
{`=DISC(A1,A2,A3,A4,1)`, `0.00068638416 ResultTypeNumber`},
|
||||||
|
{`=DISC(A1,A2,A3,A4,2)`, `0.00067650334 ResultTypeNumber`},
|
||||||
|
{`=DISC(A1,A2,A3,A4,3)`, `0.00068589922 ResultTypeNumber`},
|
||||||
|
{`=DISC(A1,A2,A3,A4,4)`, `0.00068644067 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDollarde(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=DOLLARDE(1.02,16)`, `1.125 ResultTypeNumber`},
|
||||||
|
{`=DOLLARDE(1.1,32)`, `1.3125 ResultTypeNumber`},
|
||||||
|
{`=DOLLARDE(-1.1,32)`, `-1.3125 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDollarfr(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=DOLLARFR(1.125,16)`, `1.02 ResultTypeNumber`},
|
||||||
|
{`=DOLLARFR(1.125,32)`, `1.04 ResultTypeNumber`},
|
||||||
|
{`=DOLLARFR(-1.125,32)`, `-1.04 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEffect(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=EFFECT(0.0525,4)`, `0.05354266737 ResultTypeNumber`},
|
||||||
|
{`=EFFECT(0.1,4)`, `0.10381289062 ResultTypeNumber`},
|
||||||
|
{`=EFFECT(0.1,4.5)`, `0.10381289062 ResultTypeNumber`},
|
||||||
|
{`=EFFECT(0,4.5)`, `#NUM! ResultTypeError`},
|
||||||
|
{`=EFFECT(0.1,0.5)`, `#NUM! ResultTypeError`},
|
||||||
|
{`=EFFECT("Hello world",4)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFv(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=FV(0.06/12,10,-200,-500,1)`, `2581.40337406 ResultTypeNumber`},
|
||||||
|
{`=FV(0,12,-100,-1000,1)`, `2200 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFvschedule(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.09)
|
||||||
|
sheet.Cell("A2").SetNumber(0.11)
|
||||||
|
sheet.Cell("A3").SetNumber(0.1)
|
||||||
|
sheet.Cell("A4").SetBool(true)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=FVSCHEDULE(1,A1:A3)`, `1.33089 ResultTypeNumber`},
|
||||||
|
{`=FVSCHEDULE(1,A1:A4)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIntrate(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetDate(time.Date(2008, 2, 15, 0, 0, 0, 0, time.UTC))
|
||||||
|
sheet.Cell("A2").SetDate(time.Date(2008, 5, 15, 0, 0, 0, 0, time.UTC))
|
||||||
|
sheet.Cell("A3").SetNumber(1000000)
|
||||||
|
sheet.Cell("A4").SetNumber(1014420)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=INTRATE(A1,A2,A3,A4,0)`, `0.05768 ResultTypeNumber`},
|
||||||
|
{`=INTRATE(A1,A2,A3,A4,1)`, `0.05864133333 ResultTypeNumber`},
|
||||||
|
{`=INTRATE(A1,A2,A3,A4,2)`, `0.05768 ResultTypeNumber`},
|
||||||
|
{`=INTRATE(A1,A2,A3,A4,3)`, `0.05848111111 ResultTypeNumber`},
|
||||||
|
{`=INTRATE(A1,A2,A3,A4,4)`, `0.05768 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIpmt(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.1)
|
||||||
|
sheet.Cell("A2").SetNumber(1)
|
||||||
|
sheet.Cell("A3").SetNumber(3)
|
||||||
|
sheet.Cell("A4").SetNumber(8000)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=IPMT(0.1/12,1,36,8000)`, `-66.666666666 ResultTypeNumber`},
|
||||||
|
{`=IPMT(0.1,3,3,8000)`, `-292.4471299 ResultTypeNumber`},
|
||||||
|
{`=IPMT(0.1/12,6,24,100000,1000000,0)`, `928.82357184 ResultTypeNumber`},
|
||||||
|
{`=IPMT(0.1/12,6,24,100000,1000000,1)`, `921.147343973 ResultTypeNumber`},
|
||||||
|
{`=IPMT(0.1/12,1,24,100000,1000000,1)`, `0 ResultTypeNumber`},
|
||||||
|
{`=IPMT(0.1/12,1,24,100000,1000000,0)`, `-833.33333333 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIrr(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(-70000)
|
||||||
|
sheet.Cell("A2").SetNumber(12000)
|
||||||
|
sheet.Cell("A3").SetNumber(15000)
|
||||||
|
sheet.Cell("A4").SetNumber(18000)
|
||||||
|
sheet.Cell("A5").SetNumber(21000)
|
||||||
|
sheet.Cell("A6").SetNumber(26000)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=IRR(A1:A6)`, `0.08663094803 ResultTypeNumber`},
|
||||||
|
{`=IRR(A1:A5)`, `-0.0212448482 ResultTypeNumber`},
|
||||||
|
{`=IRR(A1:A4)`, `-0.1821374641 ResultTypeNumber`},
|
||||||
|
{`=IRR(A1:A3,0.2)`, `-0.4435069413 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIspmt(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.1)
|
||||||
|
sheet.Cell("A2").SetNumber(4)
|
||||||
|
sheet.Cell("A3").SetNumber(4000)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=ISPMT(0.1/12,6,2*12,100000)`, `-625 ResultTypeNumber`},
|
||||||
|
{`=ISPMT(A1,0,A2,A3)`, `-400 ResultTypeNumber`},
|
||||||
|
{`=ISPMT(A1,1,A2,A3)`, `-300 ResultTypeNumber`},
|
||||||
|
{`=ISPMT(A1,2,A2,A3)`, `-200 ResultTypeNumber`},
|
||||||
|
{`=ISPMT(A1,3,A2,A3)`, `-100 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMirr(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(-120000)
|
||||||
|
sheet.Cell("A2").SetNumber(39000)
|
||||||
|
sheet.Cell("A3").SetNumber(30000)
|
||||||
|
sheet.Cell("A4").SetNumber(21000)
|
||||||
|
sheet.Cell("A5").SetNumber(37000)
|
||||||
|
sheet.Cell("A6").SetNumber(46000)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=MIRR(A1:A6,0.1,0.12)`, `0.12609413036 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A4,0.1,0.12)`, `-0.0480446552 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0.1,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0.2,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0.3,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0.4,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,-1,0.14)`, `0.13475911082 ResultTypeNumber`},
|
||||||
|
{`=MIRR(A1:A6,0.1,-1)`, `#DIV/0! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNominal(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=NOMINAL(0.053543,4)`, `0.05250031986 ResultTypeNumber`},
|
||||||
|
{`=NOMINAL(0.1,4)`, `0.09645475633 ResultTypeNumber`},
|
||||||
|
{`=NOMINAL(0.1,4.5)`, `0.09645475633 ResultTypeNumber`},
|
||||||
|
{`=NOMINAL(0,4.5)`, `#NUM! ResultTypeError`},
|
||||||
|
{`=NOMINAL(0.1,0.5)`, `#NUM! ResultTypeError`},
|
||||||
|
{`=NOMINAL("Hello world",4.5)`, `#VALUE! ResultTypeError`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNper(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.12)
|
||||||
|
sheet.Cell("A2").SetNumber(-100)
|
||||||
|
sheet.Cell("A3").SetNumber(-1000)
|
||||||
|
sheet.Cell("A4").SetNumber(10000)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=NPER(A1/12,A2,A3,A4,1)`, `59.6738656742 ResultTypeNumber`},
|
||||||
|
{`=NPER(A1/12,A2,A3,A4)`, `60.0821228537 ResultTypeNumber`},
|
||||||
|
{`=NPER(A1/12,A2,A3)`, `-9.5785940398 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNpv(t *testing.T) {
|
||||||
|
ss := spreadsheet.New()
|
||||||
|
sheet := ss.AddSheet()
|
||||||
|
|
||||||
|
sheet.Cell("A1").SetNumber(0.1)
|
||||||
|
sheet.Cell("A2").SetNumber(-10000)
|
||||||
|
sheet.Cell("A3").SetNumber(3000)
|
||||||
|
sheet.Cell("A4").SetNumber(4200)
|
||||||
|
sheet.Cell("A5").SetNumber(6800)
|
||||||
|
|
||||||
|
ctx := sheet.FormulaContext()
|
||||||
|
|
||||||
|
td := []testStruct{
|
||||||
|
{`=NPV(A1,A2,A3,A4,A5)`, `1188.44341233 ResultTypeNumber`},
|
||||||
|
{`=NPV(A1,A2:A4,A5)`, `1188.44341233 ResultTypeNumber`},
|
||||||
|
{`=NPV(A1,A2:A4,"Hello world",A5)`, `1188.44341233 ResultTypeNumber`},
|
||||||
|
{`=NPV(0.12,12000,15000,18000,21000,24000)`, `62448.3625219 ResultTypeNumber`},
|
||||||
|
}
|
||||||
|
|
||||||
|
runTests(t, ctx, td)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user