Skip to content

Commit 49c9ea4

Browse files
committed
ref qax-os#65: new formula function YIELD
1 parent f26df48 commit 49c9ea4

File tree

4 files changed

+135
-17
lines changed

4 files changed

+135
-17
lines changed

calc.go

+107-11
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ type formulaFuncs struct {
604604
// XOR
605605
// YEAR
606606
// YEARFRAC
607+
// YIELD
607608
// YIELDDISC
608609
// YIELDMAT
609610
// Z.TEST
@@ -1492,7 +1493,7 @@ func (fn *formulaFuncs) BESSELJ(argsList *list.List) formulaArg {
14921493
return fn.bassel(argsList, false)
14931494
}
14941495

1495-
// bassel is an implementation of the formula function BESSELI and BESSELJ.
1496+
// bassel is an implementation of the formula functions BESSELI and BESSELJ.
14961497
func (fn *formulaFuncs) bassel(argsList *list.List, modfied bool) formulaArg {
14971498
x, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber()
14981499
if x.Type != ArgNumber {
@@ -1826,7 +1827,7 @@ func (fn *formulaFuncs) BITXOR(argsList *list.List) formulaArg {
18261827
return fn.bitwise("BITXOR", argsList)
18271828
}
18281829

1829-
// bitwise is an implementation of the formula function BITAND, BITLSHIFT,
1830+
// bitwise is an implementation of the formula functions BITAND, BITLSHIFT,
18301831
// BITOR, BITRSHIFT and BITXOR.
18311832
func (fn *formulaFuncs) bitwise(name string, argsList *list.List) formulaArg {
18321833
if argsList.Len() != 2 {
@@ -1937,7 +1938,7 @@ func (fn *formulaFuncs) DEC2OCT(argsList *list.List) formulaArg {
19371938
return fn.dec2x("DEC2OCT", argsList)
19381939
}
19391940

1940-
// dec2x is an implementation of the formula function DEC2BIN, DEC2HEX and
1941+
// dec2x is an implementation of the formula functions DEC2BIN, DEC2HEX and
19411942
// DEC2OCT.
19421943
func (fn *formulaFuncs) dec2x(name string, argsList *list.List) formulaArg {
19431944
if argsList.Len() < 1 {
@@ -4586,7 +4587,7 @@ func calcStdev(stdeva bool, result, count float64, mean, token formulaArg) (floa
45864587
return result, count
45874588
}
45884589

4589-
// stdev is an implementation of the formula function STDEV and STDEVA.
4590+
// stdev is an implementation of the formula functions STDEV and STDEVA.
45904591
func (fn *formulaFuncs) stdev(stdeva bool, argsList *list.List) formulaArg {
45914592
count, result := -1.0, -1.0
45924593
var mean formulaArg
@@ -4947,7 +4948,7 @@ func (fn *formulaFuncs) CHIDIST(argsList *list.List) formulaArg {
49474948
return newNumberFormulaArg(1 - (incompleteGamma(degress.Number/2, x.Number/2) / math.Gamma(degress.Number/2)))
49484949
}
49494950

4950-
// confidence is an implementation of the formula function CONFIDENCE and
4951+
// confidence is an implementation of the formula functions CONFIDENCE and
49514952
// CONFIDENCE.NORM.
49524953
func (fn *formulaFuncs) confidence(name string, argsList *list.List) formulaArg {
49534954
if argsList.Len() != 3 {
@@ -10735,12 +10736,21 @@ func (fn *formulaFuncs) price(settlement, maturity, rate, yld, redemption, frequ
1073510736
dsc := fn.COUPDAYSNC(argsList).Number / e.Number
1073610737
n := fn.COUPNUM(argsList)
1073710738
a := fn.COUPDAYBS(argsList)
10738-
ret := redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc)
10739-
ret -= 100 * rate.Number / frequency.Number * a.Number / e.Number
10740-
t1 := 100 * rate.Number / frequency.Number
10741-
t2 := 1 + yld.Number/frequency.Number
10742-
for k := 0.0; k < n.Number; k++ {
10743-
ret += t1 / math.Pow(t2, k+dsc)
10739+
ret := 0.0
10740+
if n.Number > 1 {
10741+
ret = redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc)
10742+
ret -= 100 * rate.Number / frequency.Number * a.Number / e.Number
10743+
t1 := 100 * rate.Number / frequency.Number
10744+
t2 := 1 + yld.Number/frequency.Number
10745+
for k := 0.0; k < n.Number; k++ {
10746+
ret += t1 / math.Pow(t2, k+dsc)
10747+
}
10748+
} else {
10749+
dsc = e.Number - a.Number
10750+
t1 := 100*(rate.Number/frequency.Number) + redemption.Number
10751+
t2 := (yld.Number/frequency.Number)*(dsc/e.Number) + 1
10752+
t3 := 100 * (rate.Number / frequency.Number) * (a.Number / e.Number)
10753+
ret = t1/t2 - t3
1074410754
}
1074510755
return newNumberFormulaArg(ret)
1074610756
}
@@ -11496,6 +11506,92 @@ func (fn *formulaFuncs) XNPV(argsList *list.List) formulaArg {
1149611506
return newNumberFormulaArg(xnpv)
1149711507
}
1149811508

11509+
// yield is an implementation of the formula function YIELD.
11510+
func (fn *formulaFuncs) yield(settlement, maturity, rate, pr, redemption, frequency, basis formulaArg) formulaArg {
11511+
priceN, yield1, yield2 := newNumberFormulaArg(0), newNumberFormulaArg(0), newNumberFormulaArg(1)
11512+
price1 := fn.price(settlement, maturity, rate, yield1, redemption, frequency, basis)
11513+
if price1.Type != ArgNumber {
11514+
return price1
11515+
}
11516+
price2 := fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis)
11517+
yieldN := newNumberFormulaArg((yield2.Number - yield1.Number) * 0.5)
11518+
for iter := 0; iter < 100 && priceN.Number != pr.Number; iter++ {
11519+
priceN = fn.price(settlement, maturity, rate, yieldN, redemption, frequency, basis)
11520+
if pr.Number == price1.Number {
11521+
return yield1
11522+
} else if pr.Number == price2.Number {
11523+
return yield2
11524+
} else if pr.Number == priceN.Number {
11525+
return yieldN
11526+
} else if pr.Number < price2.Number {
11527+
yield2.Number *= 2.0
11528+
price2 = fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis)
11529+
yieldN.Number = (yield2.Number - yield1.Number) * 0.5
11530+
} else {
11531+
if pr.Number < priceN.Number {
11532+
yield1 = yieldN
11533+
price1 = priceN
11534+
} else {
11535+
yield2 = yieldN
11536+
price2 = priceN
11537+
}
11538+
yieldN.Number = yield2.Number - (yield2.Number-yield1.Number)*((pr.Number-price2.Number)/(price1.Number-price2.Number))
11539+
}
11540+
}
11541+
return yieldN
11542+
}
11543+
11544+
// YIELD function calculates the Yield of a security that pays periodic
11545+
// interest. The syntax of the function is:
11546+
//
11547+
// YIELD(settlement,maturity,rate,pr,redemption,frequency,[basis])
11548+
//
11549+
func (fn *formulaFuncs) YIELD(argsList *list.List) formulaArg {
11550+
if argsList.Len() != 6 && argsList.Len() != 7 {
11551+
return newErrorFormulaArg(formulaErrorVALUE, "YIELD requires 6 or 7 arguments")
11552+
}
11553+
args := fn.prepareDataValueArgs(2, argsList)
11554+
if args.Type != ArgList {
11555+
return args
11556+
}
11557+
settlement, maturity := args.List[0], args.List[1]
11558+
rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber()
11559+
if rate.Type != ArgNumber {
11560+
return rate
11561+
}
11562+
if rate.Number < 0 {
11563+
return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0")
11564+
}
11565+
pr := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber()
11566+
if pr.Type != ArgNumber {
11567+
return pr
11568+
}
11569+
if pr.Number <= 0 {
11570+
return newErrorFormulaArg(formulaErrorNUM, "PRICE requires pr > 0")
11571+
}
11572+
redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
11573+
if redemption.Type != ArgNumber {
11574+
return redemption
11575+
}
11576+
if redemption.Number < 0 {
11577+
return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption >= 0")
11578+
}
11579+
frequency := argsList.Front().Next().Next().Next().Next().Next().Value.(formulaArg).ToNumber()
11580+
if frequency.Type != ArgNumber {
11581+
return frequency
11582+
}
11583+
if !validateFrequency(frequency.Number) {
11584+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
11585+
}
11586+
basis := newNumberFormulaArg(0)
11587+
if argsList.Len() == 7 {
11588+
if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber {
11589+
return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM)
11590+
}
11591+
}
11592+
return fn.yield(settlement, maturity, rate, pr, redemption, frequency, basis)
11593+
}
11594+
1149911595
// YIELDDISC function calculates the annual yield of a discounted security.
1150011596
// The syntax of the function is:
1150111597
//

calc_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -1508,6 +1508,7 @@ func TestCalcCellValue(t *testing.T) {
15081508
"=PRICE(\"04/01/2012\",\"02/01/2020\",12%,10%,100,2)": "110.65510517844305",
15091509
"=PRICE(\"04/01/2012\",\"02/01/2020\",12%,10%,100,2,4)": "110.65510517844305",
15101510
"=PRICE(\"04/01/2012\",\"03/31/2020\",12%,10%,100,2)": "110.83448359321572",
1511+
"=PRICE(\"01/01/2010\",\"06/30/2010\",0.5,1,1,1,4)": "8.924190888476605",
15111512
// PPMT
15121513
"=PPMT(0.05/12,2,60,50000)": "-738.2918003208238",
15131514
"=PPMT(0.035/4,2,8,0,5000,1)": "-606.1094824182949",
@@ -1552,6 +1553,12 @@ func TestCalcCellValue(t *testing.T) {
15521553
"=VDB(24000,3000,10,0.1,1,1)": "2138.3999999999996",
15531554
"=VDB(24000,3000,100,50,100,1)": "10377.294418465235",
15541555
"=VDB(24000,3000,100,50,100,2)": "5740.072322090805",
1556+
// YIELD
1557+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4)": "0.0975631546829798",
1558+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,4)": "0.0976269355643988",
1559+
"=YIELD(\"01/01/2010\",\"06/30/2010\",0.5,1,1,1,4)": "1.91285866099894",
1560+
"=YIELD(\"01/01/2010\",\"06/30/2010\",0,1,1,1,4)": "0",
1561+
"=YIELD(\"01/01/2010\",\"01/02/2020\",100,68.15518653988686,1,1,1)": "64",
15551562
// YIELDDISC
15561563
"=YIELDDISC(\"01/01/2017\",\"06/30/2017\",97,100)": "0.0622012325059031",
15571564
"=YIELDDISC(\"01/01/2017\",\"06/30/2017\",97,100,0)": "0.0622012325059031",
@@ -3124,6 +3131,21 @@ func TestCalcCellValue(t *testing.T) {
31243131
"=VDB(10000,1000,5,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
31253132
"=VDB(10000,1000,5,0,1,\"\")": "#NUM!",
31263133
"=VDB(10000,1000,5,0,1,0.2,\"\")": "#NUM!",
3134+
// YIELD
3135+
"=YIELD()": "YIELD requires 6 or 7 arguments",
3136+
"=YIELD(\"\",\"06/30/2015\",10%,101,100,4)": "#VALUE!",
3137+
"=YIELD(\"01/01/2010\",\"\",10%,101,100,4)": "#VALUE!",
3138+
"=YIELD(\"01/01/2010\",\"06/30/2015\",\"\",101,100,4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3139+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,\"\",100,4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3140+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,\"\",4)": "strconv.ParseFloat: parsing \"\": invalid syntax",
3141+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax",
3142+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,\"\")": "#NUM!",
3143+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,3)": "#NUM!",
3144+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,5)": "invalid basis",
3145+
"=YIELD(\"01/01/2010\",\"06/30/2015\",-1,101,100,4)": "PRICE requires rate >= 0",
3146+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,0,100,4)": "PRICE requires pr > 0",
3147+
"=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,-1,4)": "PRICE requires redemption >= 0",
3148+
// "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4)": "PRICE requires rate >= 0",
31273149
// YIELDDISC
31283150
"=YIELDDISC()": "YIELDDISC requires 4 or 5 arguments",
31293151
"=YIELDDISC(\"\",\"06/30/2017\",97,100,0)": "#VALUE!",

merge.go

+4-4
Original file line numberDiff line numberDiff line change
@@ -269,15 +269,15 @@ func (m *MergeCell) GetCellValue() string {
269269
return (*m)[1]
270270
}
271271

272-
// GetStartAxis returns the merge start axis.
273-
// example: "C2"
272+
// GetStartAxis returns the top left cell coordinates of merged range, for
273+
// example: "C2".
274274
func (m *MergeCell) GetStartAxis() string {
275275
axis := strings.Split((*m)[0], ":")
276276
return axis[0]
277277
}
278278

279-
// GetEndAxis returns the merge end axis.
280-
// example: "D4"
279+
// GetEndAxis returns the bottom right cell coordinates of merged range, for
280+
// example: "D4".
281281
func (m *MergeCell) GetEndAxis() string {
282282
axis := strings.Split((*m)[0], ":")
283283
return axis[1]

styles.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -2151,8 +2151,8 @@ func (f *File) NewConditionalStyle(style string) (int, error) {
21512151
return s.Dxfs.Count - 1, nil
21522152
}
21532153

2154-
// GetDefaultFont provides the default font name currently set in the workbook
2155-
// Documents generated by excelize start with Calibri.
2154+
// GetDefaultFont provides the default font name currently set in the
2155+
// workbook. The spreadsheet generated by excelize default font is Calibri.
21562156
func (f *File) GetDefaultFont() string {
21572157
font := f.readDefaultFont()
21582158
return *font.Name.Val

0 commit comments

Comments
 (0)