summaryrefslogtreecommitdiff
path: root/calc.go
diff options
context:
space:
mode:
Diffstat (limited to 'calc.go')
-rw-r--r--calc.go138
1 files changed, 120 insertions, 18 deletions
diff --git a/calc.go b/calc.go
index 97ba373..7d27cb8 100644
--- a/calc.go
+++ b/calc.go
@@ -296,6 +296,7 @@ type formulaFuncs struct {
// Supported formula functions:
//
// ABS
+// ACCRINT
// ACCRINTM
// ACOS
// ACOSH
@@ -439,6 +440,7 @@ type formulaFuncs struct {
// ISODD
// ISTEXT
// ISO.CEILING
+// ISOWEEKNUM
// ISPMT
// KURT
// LARGE
@@ -7025,6 +7027,39 @@ func (fn *formulaFuncs) DAYS(argsList *list.List) formulaArg {
return newNumberFormulaArg(end - start)
}
+// ISOWEEKNUM function returns the ISO week number of a supplied date. The
+// syntax of the function is:
+//
+// ISOWEEKNUM(date)
+//
+func (fn *formulaFuncs) ISOWEEKNUM(argsList *list.List) formulaArg {
+ if argsList.Len() != 1 {
+ return newErrorFormulaArg(formulaErrorVALUE, "ISOWEEKNUM requires 1 argument")
+ }
+ date := argsList.Front().Value.(formulaArg)
+ num := date.ToNumber()
+ weeknum := 0
+ if num.Type != ArgNumber {
+ dateString := strings.ToLower(date.Value())
+ if !isDateOnlyFmt(dateString) {
+ if _, _, _, _, _, err := strToTime(dateString); err.Type == ArgError {
+ return err
+ }
+ }
+ y, m, d, _, err := strToDate(dateString)
+ if err.Type == ArgError {
+ return err
+ }
+ _, weeknum = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.UTC).ISOWeek()
+ } else {
+ if num.Number < 0 {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ _, weeknum = timeFromExcelTime(num.Number, false).ISOWeek()
+ }
+ return newNumberFormulaArg(float64(weeknum))
+}
+
// MONTH function returns the month of a date represented by a serial number.
// The month is given as an integer, ranging from 1 (January) to 12
// (December). The syntax of the function is:
@@ -7317,7 +7352,6 @@ func (fn *formulaFuncs) WEEKDAY(argsList *list.List) formulaArg {
if argsList.Len() > 2 {
return newErrorFormulaArg(formulaErrorVALUE, "WEEKDAY allows at most 2 arguments")
}
-
sn := argsList.Front().Value.(formulaArg)
num := sn.ToNumber()
weekday, returnType := 0, 1
@@ -8915,6 +8949,69 @@ func (fn *formulaFuncs) ENCODEURL(argsList *list.List) formulaArg {
// Financial Functions
+// validateFrequency check the number of coupon payments per year if be equal to 1, 2 or 4.
+func validateFrequency(freq float64) bool {
+ return freq == 1 || freq == 2 || freq == 4
+}
+
+// ACCRINT function returns the accrued interest for a security that pays
+// periodic interest. The syntax of the function is:
+//
+// ACCRINT(issue,first_interest,settlement,rate,par,frequency,[basis],[calc_method])
+//
+func (fn *formulaFuncs) ACCRINT(argsList *list.List) formulaArg {
+ if argsList.Len() < 6 {
+ return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT requires at least 6 arguments")
+ }
+ if argsList.Len() > 8 {
+ return newErrorFormulaArg(formulaErrorVALUE, "ACCRINT allows at most 8 arguments")
+ }
+ args := list.New().Init()
+ args.PushBack(argsList.Front().Value.(formulaArg))
+ issue := fn.DATEVALUE(args)
+ if issue.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ args.Init()
+ args.PushBack(argsList.Front().Next().Value.(formulaArg))
+ fi := fn.DATEVALUE(args)
+ if fi.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ args.Init()
+ args.PushBack(argsList.Front().Next().Next().Value.(formulaArg))
+ settlement := fn.DATEVALUE(args)
+ if settlement.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ rate := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
+ par := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
+ frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
+ if rate.Type != ArgNumber || par.Type != ArgNumber || frequency.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ if !validateFrequency(frequency.Number) {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ basis := newNumberFormulaArg(0)
+ if argsList.Len() >= 7 {
+ if basis = argsList.Front().Next().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
+ }
+ }
+ cm := newBoolFormulaArg(true)
+ if argsList.Len() == 8 {
+ if cm = argsList.Back().Value.(formulaArg).ToBool(); cm.Type != ArgNumber {
+ return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
+ }
+ }
+ frac1 := yearFrac(issue.Number, settlement.Number, int(basis.Number))
+ if frac1.Type != ArgNumber {
+ return frac1
+ }
+ return newNumberFormulaArg(par.Number * rate.Number * frac1.Number)
+}
+
// ACCRINTM function returns the accrued interest for a security that pays
// interest at maturity. The syntax of the function is:
//
@@ -10023,6 +10120,27 @@ func (fn *formulaFuncs) PV(argsList *list.List) formulaArg {
return newNumberFormulaArg((((1-math.Pow(1+rate.Number, nper.Number))/rate.Number)*pmt.Number*(1+rate.Number*t.Number) - fv.Number) / math.Pow(1+rate.Number, nper.Number))
}
+// rate is an implementation of the formula function RATE.
+func (fn *formulaFuncs) rate(nper, pmt, pv, fv, t, guess formulaArg, argsList *list.List) formulaArg {
+ maxIter, iter, close, epsMax, rate := 100, 0, false, 1e-6, guess.Number
+ for iter < maxIter && !close {
+ t1 := math.Pow(rate+1, nper.Number)
+ t2 := math.Pow(rate+1, nper.Number-1)
+ rt := rate*t.Number + 1
+ p0 := pmt.Number * (t1 - 1)
+ f1 := fv.Number + t1*pv.Number + p0*rt/rate
+ f2 := nper.Number*t2*pv.Number - p0*rt/math.Pow(rate, 2)
+ f3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate
+ delta := f1 / (f2 + f3)
+ if math.Abs(delta) < epsMax {
+ close = true
+ }
+ iter++
+ rate -= delta
+ }
+ return newNumberFormulaArg(rate)
+}
+
// RATE function calculates the interest rate required to pay off a specified
// amount of a loan, or to reach a target amount on an investment, over a
// given period. The syntax of the function is:
@@ -10069,23 +10187,7 @@ func (fn *formulaFuncs) RATE(argsList *list.List) formulaArg {
return guess
}
}
- maxIter, iter, close, epsMax, rate := 100, 0, false, 1e-6, guess.Number
- for iter < maxIter && !close {
- t1 := math.Pow(rate+1, nper.Number)
- t2 := math.Pow(rate+1, nper.Number-1)
- rt := rate*t.Number + 1
- p0 := pmt.Number * (t1 - 1)
- f1 := fv.Number + t1*pv.Number + p0*rt/rate
- f2 := nper.Number*t2*pv.Number - p0*rt/math.Pow(rate, 2)
- f3 := (nper.Number*pmt.Number*t2*rt + p0*t.Number) / rate
- delta := f1 / (f2 + f3)
- if math.Abs(delta) < epsMax {
- close = true
- }
- iter++
- rate -= delta
- }
- return newNumberFormulaArg(rate)
+ return fn.rate(nper, pmt, pv, fv, t, guess, argsList)
}
// RECEIVED function calculates the amount received at maturity for a fully