Update docs for SetSheetStyle, and added 2 formula functions

ref #65: new formula functions DURATION and MDURATION
fix incorrect example in SetSheetStyle docs
This commit is contained in:
xuri 2021-12-04 13:07:58 +08:00
parent 577a07f08c
commit e0c6fa1beb
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7
3 changed files with 142 additions and 2 deletions

110
calc.go
View File

@ -379,6 +379,7 @@ type formulaFuncs struct {
// DISC
// DOLLARDE
// DOLLARFR
// DURATION
// EFFECT
// ENCODEURL
// ERF
@ -471,6 +472,7 @@ type formulaFuncs struct {
// MATCH
// MAX
// MDETERM
// MDURATION
// MEDIAN
// MID
// MIDB
@ -10179,6 +10181,96 @@ func (fn *formulaFuncs) dollar(name string, argsList *list.List) formulaArg {
return newNumberFormulaArg(math.Floor(dollar.Number) + cents)
}
// prepareDurationArgs checking and prepare arguments for the formula
// functions DURATION and MDURATION.
func (fn *formulaFuncs) prepareDurationArgs(name string, argsList *list.List) formulaArg {
if argsList.Len() != 5 && argsList.Len() != 6 {
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 5 or 6 arguments", name))
}
args := fn.prepareDataValueArgs(2, argsList)
if args.Type != ArgList {
return args
}
settlement, maturity := args.List[0], args.List[1]
if settlement.Number >= maturity.Number {
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name))
}
coupon := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if coupon.Type != ArgNumber {
return coupon
}
if coupon.Number < 0 {
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires coupon >= 0", name))
}
yld := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
if yld.Type != ArgNumber {
return yld
}
if yld.Number < 0 {
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires yld >= 0", name))
}
frequency := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
if frequency.Type != ArgNumber {
return frequency
}
if !validateFrequency(frequency.Number) {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
basis := newNumberFormulaArg(0)
if argsList.Len() == 6 {
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
}
return newListFormulaArg([]formulaArg{settlement, maturity, coupon, yld, frequency, basis})
}
// duration is an implementation of the formula function DURATION.
func (fn *formulaFuncs) duration(settlement, maturity, coupon, yld, frequency, basis formulaArg) formulaArg {
frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))
if frac.Type != ArgNumber {
return frac
}
argumments := list.New().Init()
argumments.PushBack(settlement)
argumments.PushBack(maturity)
argumments.PushBack(frequency)
argumments.PushBack(basis)
coups := fn.COUPNUM(argumments)
duration := 0.0
p := 0.0
coupon.Number *= 100 / frequency.Number
yld.Number /= frequency.Number
yld.Number++
diff := frac.Number*frequency.Number - coups.Number
for t := 1.0; t < coups.Number; t++ {
tDiff := t + diff
add := coupon.Number / math.Pow(yld.Number, tDiff)
p += add
duration += tDiff * add
}
add := (coupon.Number + 100) / math.Pow(yld.Number, coups.Number+diff)
p += add
duration += (coups.Number + diff) * add
duration /= p
duration /= frequency.Number
return newNumberFormulaArg(duration)
}
// DURATION function calculates the Duration (specifically, the Macaulay
// Duration) of a security that pays periodic interest, assuming a par value
// of $100. The syntax of the function is:
//
// DURATION(settlement,maturity,coupon,yld,frequency,[basis])
//
func (fn *formulaFuncs) DURATION(argsList *list.List) formulaArg {
args := fn.prepareDurationArgs("DURATION", argsList)
if args.Type != ArgList {
return args
}
return fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5])
}
// EFFECT function returns the effective annual interest rate for a given
// nominal interest rate and number of compounding periods per year. The
// syntax of the function is:
@ -10504,6 +10596,24 @@ func (fn *formulaFuncs) ISPMT(argsList *list.List) formulaArg {
return newNumberFormulaArg(num)
}
// MDURATION function calculates the Modified Macaulay Duration of a security
// that pays periodic interest, assuming a par value of $100. The syntax of
// the function is:
//
// MDURATION(settlement,maturity,coupon,yld,frequency,[basis])
//
func (fn *formulaFuncs) MDURATION(argsList *list.List) formulaArg {
args := fn.prepareDurationArgs("MDURATION", argsList)
if args.Type != ArgList {
return args
}
duration := fn.duration(args.List[0], args.List[1], args.List[2], args.List[3], args.List[4], args.List[5])
if duration.Type != ArgNumber {
return duration
}
return newNumberFormulaArg(duration.Number / (1 + args.List[3].Number/args.List[4].Number))
}
// MIRR function returns the Modified Internal Rate of Return for a supplied
// series of periodic cash flows (i.e. a set of values, which includes an
// initial investment value and a series of net income values). The syntax of

View File

@ -1472,6 +1472,8 @@ func TestCalcCellValue(t *testing.T) {
"=DOLLARDE(1.01,16)": "1.0625",
// DOLLARFR
"=DOLLARFR(1.0625,16)": "1.01",
// DURATION
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4)": "6.674422798483131",
// EFFECT
"=EFFECT(0.1,4)": "0.103812890625",
"=EFFECT(0.025,2)": "0.02515625",
@ -1491,6 +1493,8 @@ func TestCalcCellValue(t *testing.T) {
"=ISPMT(0.05/12,1,60,50000)": "-204.8611111111111",
"=ISPMT(0.05/12,2,60,50000)": "-201.38888888888886",
"=ISPMT(0.05/12,2,1,50000)": "208.33333333333334",
// MDURATION
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4)": "6.543551763218756",
// NOMINAL
"=NOMINAL(0.025,12)": "0.0247180352381129",
// NPER
@ -2916,6 +2920,19 @@ func TestCalcCellValue(t *testing.T) {
"=DOLLARFR(0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DOLLARFR(0,-1)": "#NUM!",
"=DOLLARFR(0,0)": "#DIV/0!",
// DURATION
"=DURATION()": "DURATION requires 5 or 6 arguments",
"=DURATION(\"\",\"03/31/2025\",10%,8%,4)": "#VALUE!",
"=DURATION(\"04/01/2015\",\"\",10%,8%,4)": "#VALUE!",
"=DURATION(\"03/31/2025\",\"04/01/2015\",10%,8%,4)": "DURATION requires maturity > settlement",
"=DURATION(\"04/01/2015\",\"03/31/2025\",-1,8%,4)": "DURATION requires coupon >= 0",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,-1,4)": "DURATION requires yld >= 0",
"=DURATION(\"04/01/2015\",\"03/31/2025\",\"\",8%,4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,\"\",4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,3)": "#NUM!",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4,\"\")": "#NUM!",
"=DURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4,5)": "invalid basis",
// EFFECT
"=EFFECT()": "EFFECT requires 2 arguments",
"=EFFECT(\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
@ -2964,6 +2981,19 @@ func TestCalcCellValue(t *testing.T) {
"=ISPMT(0,\"\",0,0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=ISPMT(0,0,\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=ISPMT(0,0,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
// MDURATION
"=MDURATION()": "MDURATION requires 5 or 6 arguments",
"=MDURATION(\"\",\"03/31/2025\",10%,8%,4)": "#VALUE!",
"=MDURATION(\"04/01/2015\",\"\",10%,8%,4)": "#VALUE!",
"=MDURATION(\"03/31/2025\",\"04/01/2015\",10%,8%,4)": "MDURATION requires maturity > settlement",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",-1,8%,4)": "MDURATION requires coupon >= 0",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,-1,4)": "MDURATION requires yld >= 0",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",\"\",8%,4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,\"\",4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,3)": "#NUM!",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4,\"\")": "#NUM!",
"=MDURATION(\"04/01/2015\",\"03/31/2025\",10%,8%,4,5)": "invalid basis",
// NOMINAL
"=NOMINAL()": "NOMINAL requires 2 arguments",
"=NOMINAL(\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",

View File

@ -774,11 +774,11 @@ func checkRow(ws *xlsxWorksheet) error {
//
// For example set style of row 1 on Sheet1:
//
// err = f.SetRowStyle("Sheet1", 1, style)
// err = f.SetRowStyle("Sheet1", 1, 1, styleID)
//
// Set style of rows 1 to 10 on Sheet1:
//
// err = f.SetRowStyle("Sheet1", 1, 10, style)
// err = f.SetRowStyle("Sheet1", 1, 10, styleID)
//
func (f *File) SetRowStyle(sheet string, start, end, styleID int) error {
if end < start {