#65 fn: IRR nad MIRR

formula
xuri 4 years ago
parent 471f4f8d2b
commit 80d832022f
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -55,6 +55,8 @@ const (
// displays only the leading 15 figures. In the second line, the number one // displays only the leading 15 figures. In the second line, the number one
// is added to the fraction, and again Excel displays only 15 figures. // is added to the fraction, and again Excel displays only 15 figures.
const numericPrecision = 1000000000000000 const numericPrecision = 1000000000000000
const maxFinancialIterations = 128
const financialPercision = 1.0e-08
// cellRef defines the structure of a cell reference. // cellRef defines the structure of a cell reference.
type cellRef struct { type cellRef struct {
@ -329,6 +331,7 @@ var tokenPriority = map[string]int{
// IMTAN // IMTAN
// INT // INT
// IPMT // IPMT
// IRR
// ISBLANK // ISBLANK
// ISERR // ISERR
// ISERROR // ISERROR
@ -359,6 +362,7 @@ var tokenPriority = map[string]int{
// MIDB // MIDB
// MIN // MIN
// MINA // MINA
// MIRR
// MOD // MOD
// MROUND // MROUND
// MULTINOMIAL // MULTINOMIAL
@ -7573,6 +7577,76 @@ func (fn *formulaFuncs) ipmt(name string, argsList *list.List) formulaArg {
return newNumberFormulaArg(principal) return newNumberFormulaArg(principal)
} }
// IRR function returns the Internal Rate of Return for a supplied series of
// periodic cash flows (i.e. an initial investment value and a series of net
// income values). The syntax of the function is:
//
// IRR(values,[guess])
//
func (fn *formulaFuncs) IRR(argsList *list.List) formulaArg {
if argsList.Len() < 1 {
return newErrorFormulaArg(formulaErrorVALUE, "IRR requires at least 1 argument")
}
if argsList.Len() > 2 {
return newErrorFormulaArg(formulaErrorVALUE, "IRR allows at most 2 arguments")
}
values, guess := argsList.Front().Value.(formulaArg).ToList(), newNumberFormulaArg(0.1)
if argsList.Len() > 1 {
if guess = argsList.Back().Value.(formulaArg).ToNumber(); guess.Type != ArgNumber {
return guess
}
}
x1, x2 := newNumberFormulaArg(0), guess
args := list.New().Init()
args.PushBack(x1)
for _, v := range values {
args.PushBack(v)
}
f1 := fn.NPV(args)
args.Front().Value = x2
f2 := fn.NPV(args)
for i := 0; i < maxFinancialIterations; i++ {
if f1.Number*f2.Number < 0 {
break
}
if math.Abs(f1.Number) < math.Abs((f2.Number)) {
x1.Number += 1.6 * (x1.Number - x2.Number)
args.Front().Value = x1
f1 = fn.NPV(args)
continue
}
x2.Number += 1.6 * (x2.Number - x1.Number)
args.Front().Value = x2
f2 = fn.NPV(args)
}
if f1.Number*f2.Number > 0 {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
args.Front().Value = x1
f := fn.NPV(args)
var rtb, dx, xMid, fMid float64
if f.Number < 0 {
rtb = x1.Number
dx = x2.Number - x1.Number
} else {
rtb = x2.Number
dx = x1.Number - x2.Number
}
for i := 0; i < maxFinancialIterations; i++ {
dx *= 0.5
xMid = rtb + dx
args.Front().Value = newNumberFormulaArg(xMid)
fMid = fn.NPV(args).Number
if fMid <= 0 {
rtb = xMid
}
if math.Abs(fMid) < financialPercision || math.Abs(dx) < financialPercision {
break
}
}
return newNumberFormulaArg(xMid)
}
// ISPMT function calculates the interest paid during a specific period of a // ISPMT function calculates the interest paid during a specific period of a
// loan or investment. The syntax of the function is: // loan or investment. The syntax of the function is:
// //
@ -7609,6 +7683,41 @@ func (fn *formulaFuncs) ISPMT(argsList *list.List) formulaArg {
return newNumberFormulaArg(num) return newNumberFormulaArg(num)
} }
// 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
// the function is:
//
// MIRR(values,finance_rate,reinvest_rate)
//
func (fn *formulaFuncs) MIRR(argsList *list.List) formulaArg {
if argsList.Len() != 3 {
return newErrorFormulaArg(formulaErrorVALUE, "MIRR requires 3 arguments")
}
values := argsList.Front().Value.(formulaArg).ToList()
financeRate := argsList.Front().Next().Value.(formulaArg).ToNumber()
if financeRate.Type != ArgNumber {
return financeRate
}
reinvestRate := argsList.Back().Value.(formulaArg).ToNumber()
if reinvestRate.Type != ArgNumber {
return reinvestRate
}
n, fr, rr, npvPos, npvNeg := len(values), 1+financeRate.Number, 1+reinvestRate.Number, 0.0, 0.0
for i, v := range values {
val := v.ToNumber()
if val.Number >= 0 {
npvPos += val.Number / math.Pow(float64(rr), float64(i))
continue
}
npvNeg += val.Number / math.Pow(float64(fr), float64(i))
}
if npvNeg == 0 || npvPos == 0 || reinvestRate.Number <= -1 {
return newErrorFormulaArg(formulaErrorDIV, formulaErrorDIV)
}
return newNumberFormulaArg(math.Pow(-npvPos*math.Pow(rr, float64(n))/(npvNeg*rr), 1/(float64(n)-1)) - 1)
}
// NOMINAL function returns the nominal interest rate for a given effective // NOMINAL function returns the nominal interest rate for a given effective
// interest rate and number of compounding periods per year. The syntax of // interest rate and number of compounding periods per year. The syntax of
// the function is: // the function is:

@ -2518,3 +2518,58 @@ func TestCalcHLOOKUP(t *testing.T) {
assert.Equal(t, "", result, formula) assert.Equal(t, "", result, formula)
} }
} }
func TestCalcIRR(t *testing.T) {
cellData := [][]interface{}{{-1}, {0.2}, {0.24}, {0.288}, {0.3456}, {0.4147}}
f := prepareCalcData(cellData)
formulaList := map[string]string{
"=IRR(A1:A4)": "-0.136189509034157",
"=IRR(A1:A6)": "0.130575760006905",
"=IRR(A1:A4,-0.1)": "-0.136189514994621",
}
for formula, expected := range formulaList {
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", formula))
result, err := f.CalcCellValue("Sheet1", "B1")
assert.NoError(t, err, formula)
assert.Equal(t, expected, result, formula)
}
calcError := map[string]string{
"=IRR()": "IRR requires at least 1 argument",
"=IRR(0,0,0)": "IRR allows at most 2 arguments",
"=IRR(0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=IRR(A2:A3)": "#NUM!",
}
for formula, expected := range calcError {
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", formula))
result, err := f.CalcCellValue("Sheet1", "B1")
assert.EqualError(t, err, expected, formula)
assert.Equal(t, "", result, formula)
}
}
func TestCalcMIRR(t *testing.T) {
cellData := [][]interface{}{{-100}, {18}, {22.5}, {28}, {35.5}, {45}}
f := prepareCalcData(cellData)
formulaList := map[string]string{
"=MIRR(A1:A5,0.055,0.05)": "0.025376365108071",
"=MIRR(A1:A6,0.055,0.05)": "0.1000268752662",
}
for formula, expected := range formulaList {
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", formula))
result, err := f.CalcCellValue("Sheet1", "B1")
assert.NoError(t, err, formula)
assert.Equal(t, expected, result, formula)
}
calcError := map[string]string{
"=MIRR()": "MIRR requires 3 arguments",
"=MIRR(A1:A5,\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=MIRR(A1:A5,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=MIRR(B1:B5,0,0)": "#DIV/0!",
}
for formula, expected := range calcError {
assert.NoError(t, f.SetCellFormula("Sheet1", "B1", formula))
result, err := f.CalcCellValue("Sheet1", "B1")
assert.EqualError(t, err, expected, formula)
assert.Equal(t, "", result, formula)
}
}

Loading…
Cancel
Save