ref #65: new formula functions DISC and INTRATE

pull/2/head
xuri 3 years ago
parent 1df7f32cc6
commit 49e80b9e47
No known key found for this signature in database
GPG Key ID: BA5E5BB1C948EDF7

@ -327,6 +327,7 @@ type formulaFuncs struct {
// DEC2OCT
// DECIMAL
// DEGREES
// DISC
// DOLLARDE
// DOLLARFR
// EFFECT
@ -385,6 +386,7 @@ type formulaFuncs struct {
// IMSUM
// IMTAN
// INT
// INTRATE
// IPMT
// IRR
// ISBLANK
@ -8773,6 +8775,57 @@ func (fn *formulaFuncs) DDB(argsList *list.List) formulaArg {
return newNumberFormulaArg(depreciation)
}
// DISC function calculates the Discount Rate for a security. The syntax of
// the function is:
//
// DISC(settlement,maturity,pr,redemption,[basis])
//
func (fn *formulaFuncs) DISC(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "DISC requires 4 or 5 arguments")
}
args := list.New().Init()
args.PushBack(argsList.Front().Value.(formulaArg))
settlement := fn.DATEVALUE(args)
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "DISC requires maturity > settlement")
}
pr := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if pr.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if pr.Number <= 0 {
return newErrorFormulaArg(formulaErrorNUM, "DISC requires pr > 0")
}
redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
if redemption.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if redemption.Number <= 0 {
return newErrorFormulaArg(formulaErrorNUM, "DISC requires redemption > 0")
}
basis := newNumberFormulaArg(0)
if argsList.Len() == 5 {
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
}
frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))
if frac.Type != ArgNumber {
return frac
}
return newNumberFormulaArg((redemption.Number - pr.Number) / redemption.Number / frac.Number)
}
// DOLLARDE function converts a dollar value in fractional notation, into a
// dollar value expressed as a decimal. The syntax of the function is:
//
@ -8918,6 +8971,57 @@ func (fn *formulaFuncs) FVSCHEDULE(argsList *list.List) formulaArg {
return newNumberFormulaArg(principal)
}
// INTRATE function calculates the interest rate for a fully invested
// security. The syntax of the function is:
//
// INTRATE(settlement,maturity,investment,redemption,[basis])
//
func (fn *formulaFuncs) INTRATE(argsList *list.List) formulaArg {
if argsList.Len() != 4 && argsList.Len() != 5 {
return newErrorFormulaArg(formulaErrorVALUE, "INTRATE requires 4 or 5 arguments")
}
args := list.New().Init()
args.PushBack(argsList.Front().Value.(formulaArg))
settlement := fn.DATEVALUE(args)
if settlement.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
args.Init()
args.PushBack(argsList.Front().Next().Value.(formulaArg))
maturity := fn.DATEVALUE(args)
if maturity.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if maturity.Number <= settlement.Number {
return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires maturity > settlement")
}
investment := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
if investment.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if investment.Number <= 0 {
return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires investment > 0")
}
redemption := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
if redemption.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
}
if redemption.Number <= 0 {
return newErrorFormulaArg(formulaErrorNUM, "INTRATE requires redemption > 0")
}
basis := newNumberFormulaArg(0)
if argsList.Len() == 5 {
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
}
}
frac := yearFrac(settlement.Number, maturity.Number, int(basis.Number))
if frac.Type != ArgNumber {
return frac
}
return newNumberFormulaArg((redemption.Number - investment.Number) / investment.Number / frac.Number)
}
// IPMT function calculates the interest payment, during a specific period of a
// loan or investment that is paid in constant periodic payments, with a
// constant interest rate. The syntax of the function is:

@ -1286,6 +1286,8 @@ func TestCalcCellValue(t *testing.T) {
"=DDB(10000,1000,5,3)": "1440",
"=DDB(10000,1000,5,4)": "864",
"=DDB(10000,1000,5,5)": "296",
// DISC
"=DISC(\"04/01/2016\",\"03/31/2021\",95,100)": "0.01",
// DOLLARDE
"=DOLLARDE(1.01,16)": "1.0625",
// DOLLARFR
@ -1300,6 +1302,8 @@ func TestCalcCellValue(t *testing.T) {
// FVSCHEDULE
"=FVSCHEDULE(10000,A1:A5)": "240000",
"=FVSCHEDULE(10000,0.5)": "15000",
// INTRATE
"=INTRATE(\"04/01/2005\",\"03/31/2010\",1000,2125)": "0.225",
// IPMT
"=IPMT(0.05/12,2,60,50000)": "-205.26988187971995",
"=IPMT(0.035/4,2,8,0,5000,1)": "5.257455237829077",
@ -2428,6 +2432,17 @@ func TestCalcCellValue(t *testing.T) {
"=DDB(0,0,\"\",0,0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DDB(0,0,0,\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=DDB(0,0,0,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
// DISC
"=DISC()": "DISC requires 4 or 5 arguments",
"=DISC(\"\",\"03/31/2021\",95,100)": "#VALUE!",
"=DISC(\"04/01/2016\",\"\",95,100)": "#VALUE!",
"=DISC(\"04/01/2016\",\"03/31/2021\",\"\",100)": "#VALUE!",
"=DISC(\"04/01/2016\",\"03/31/2021\",95,\"\")": "#VALUE!",
"=DISC(\"04/01/2016\",\"03/31/2021\",95,100,\"\")": "#NUM!",
"=DISC(\"03/31/2021\",\"04/01/2016\",95,100)": "DISC requires maturity > settlement",
"=DISC(\"04/01/2016\",\"03/31/2021\",0,100)": "DISC requires pr > 0",
"=DISC(\"04/01/2016\",\"03/31/2021\",95,0)": "DISC requires redemption > 0",
"=DISC(\"04/01/2016\",\"03/31/2021\",95,100,5)": "invalid basis",
// DOLLARDE
"=DOLLARDE()": "DOLLARDE requires 2 arguments",
"=DOLLARDE(\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
@ -2459,6 +2474,17 @@ func TestCalcCellValue(t *testing.T) {
"=FVSCHEDULE()": "FVSCHEDULE requires 2 arguments",
"=FVSCHEDULE(\"\",0)": "strconv.ParseFloat: parsing \"\": invalid syntax",
"=FVSCHEDULE(0,\"x\")": "strconv.ParseFloat: parsing \"x\": invalid syntax",
// INTRATE
"=INTRATE()": "INTRATE requires 4 or 5 arguments",
"=INTRATE(\"\",\"03/31/2021\",95,100)": "#VALUE!",
"=INTRATE(\"04/01/2016\",\"\",95,100)": "#VALUE!",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",\"\",100)": "#VALUE!",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",95,\"\")": "#VALUE!",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",95,100,\"\")": "#NUM!",
"=INTRATE(\"03/31/2021\",\"04/01/2016\",95,100)": "INTRATE requires maturity > settlement",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",0,100)": "INTRATE requires investment > 0",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",95,0)": "INTRATE requires redemption > 0",
"=INTRATE(\"04/01/2016\",\"03/31/2021\",95,100,5)": "invalid basis",
// IPMT
"=IPMT()": "IPMT requires at least 4 arguments",
"=IPMT(0,0,0,0,0,0,0)": "IPMT allows at most 6 arguments",

Loading…
Cancel
Save