mirror of
https://github.com/unidoc/unioffice.git
synced 2025-04-25 13:48:53 +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
|
||||
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
|
||||
|
@ -10,13 +10,11 @@ package formula
|
||||
import (
|
||||
"time"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
RegisterFunction("DURATION", Duration)
|
||||
RegisterFunction("MDURATION", Mduration)
|
||||
RegisterFunction("PDURATION", Pduration)
|
||||
RegisterFunction("_xlfn.PDURATION", Pduration)
|
||||
RegisterFunction("ACCRINTM", Accrintm)
|
||||
RegisterFunction("AMORDEGRC", Amordegrc)
|
||||
RegisterFunction("AMORLINC", Amorlinc)
|
||||
@ -28,6 +26,26 @@ func init() {
|
||||
RegisterFunction("COUPPCD", Couppcd)
|
||||
RegisterFunction("CUMIPMT", Cumipmt)
|
||||
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.
|
||||
@ -789,3 +807,775 @@ func fv(rate, periods, payment, value float64, t int) float64 {
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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