diff --git a/calc.go b/calc.go index a161dda..91199cc 100644 --- a/calc.go +++ b/calc.go @@ -604,6 +604,7 @@ type formulaFuncs struct { // XOR // YEAR // YEARFRAC +// YIELD // YIELDDISC // YIELDMAT // Z.TEST @@ -1492,7 +1493,7 @@ func (fn *formulaFuncs) BESSELJ(argsList *list.List) formulaArg { return fn.bassel(argsList, false) } -// bassel is an implementation of the formula function BESSELI and BESSELJ. +// bassel is an implementation of the formula functions BESSELI and BESSELJ. func (fn *formulaFuncs) bassel(argsList *list.List, modfied bool) formulaArg { x, n := argsList.Front().Value.(formulaArg).ToNumber(), argsList.Back().Value.(formulaArg).ToNumber() if x.Type != ArgNumber { @@ -1826,7 +1827,7 @@ func (fn *formulaFuncs) BITXOR(argsList *list.List) formulaArg { return fn.bitwise("BITXOR", argsList) } -// bitwise is an implementation of the formula function BITAND, BITLSHIFT, +// bitwise is an implementation of the formula functions BITAND, BITLSHIFT, // BITOR, BITRSHIFT and BITXOR. func (fn *formulaFuncs) bitwise(name string, argsList *list.List) formulaArg { if argsList.Len() != 2 { @@ -1937,7 +1938,7 @@ func (fn *formulaFuncs) DEC2OCT(argsList *list.List) formulaArg { return fn.dec2x("DEC2OCT", argsList) } -// dec2x is an implementation of the formula function DEC2BIN, DEC2HEX and +// dec2x is an implementation of the formula functions DEC2BIN, DEC2HEX and // DEC2OCT. func (fn *formulaFuncs) dec2x(name string, argsList *list.List) formulaArg { if argsList.Len() < 1 { @@ -4586,7 +4587,7 @@ func calcStdev(stdeva bool, result, count float64, mean, token formulaArg) (floa return result, count } -// stdev is an implementation of the formula function STDEV and STDEVA. +// stdev is an implementation of the formula functions STDEV and STDEVA. func (fn *formulaFuncs) stdev(stdeva bool, argsList *list.List) formulaArg { count, result := -1.0, -1.0 var mean formulaArg @@ -4947,7 +4948,7 @@ func (fn *formulaFuncs) CHIDIST(argsList *list.List) formulaArg { return newNumberFormulaArg(1 - (incompleteGamma(degress.Number/2, x.Number/2) / math.Gamma(degress.Number/2))) } -// confidence is an implementation of the formula function CONFIDENCE and +// confidence is an implementation of the formula functions CONFIDENCE and // CONFIDENCE.NORM. func (fn *formulaFuncs) confidence(name string, argsList *list.List) formulaArg { if argsList.Len() != 3 { @@ -10735,12 +10736,21 @@ func (fn *formulaFuncs) price(settlement, maturity, rate, yld, redemption, frequ dsc := fn.COUPDAYSNC(argsList).Number / e.Number n := fn.COUPNUM(argsList) a := fn.COUPDAYBS(argsList) - ret := redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc) - ret -= 100 * rate.Number / frequency.Number * a.Number / e.Number - t1 := 100 * rate.Number / frequency.Number - t2 := 1 + yld.Number/frequency.Number - for k := 0.0; k < n.Number; k++ { - ret += t1 / math.Pow(t2, k+dsc) + ret := 0.0 + if n.Number > 1 { + ret = redemption.Number / math.Pow(1+yld.Number/frequency.Number, n.Number-1+dsc) + ret -= 100 * rate.Number / frequency.Number * a.Number / e.Number + t1 := 100 * rate.Number / frequency.Number + t2 := 1 + yld.Number/frequency.Number + for k := 0.0; k < n.Number; k++ { + ret += t1 / math.Pow(t2, k+dsc) + } + } else { + dsc = e.Number - a.Number + t1 := 100*(rate.Number/frequency.Number) + redemption.Number + t2 := (yld.Number/frequency.Number)*(dsc/e.Number) + 1 + t3 := 100 * (rate.Number / frequency.Number) * (a.Number / e.Number) + ret = t1/t2 - t3 } return newNumberFormulaArg(ret) } @@ -11496,6 +11506,92 @@ func (fn *formulaFuncs) XNPV(argsList *list.List) formulaArg { return newNumberFormulaArg(xnpv) } +// yield is an implementation of the formula function YIELD. +func (fn *formulaFuncs) yield(settlement, maturity, rate, pr, redemption, frequency, basis formulaArg) formulaArg { + priceN, yield1, yield2 := newNumberFormulaArg(0), newNumberFormulaArg(0), newNumberFormulaArg(1) + price1 := fn.price(settlement, maturity, rate, yield1, redemption, frequency, basis) + if price1.Type != ArgNumber { + return price1 + } + price2 := fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis) + yieldN := newNumberFormulaArg((yield2.Number - yield1.Number) * 0.5) + for iter := 0; iter < 100 && priceN.Number != pr.Number; iter++ { + priceN = fn.price(settlement, maturity, rate, yieldN, redemption, frequency, basis) + if pr.Number == price1.Number { + return yield1 + } else if pr.Number == price2.Number { + return yield2 + } else if pr.Number == priceN.Number { + return yieldN + } else if pr.Number < price2.Number { + yield2.Number *= 2.0 + price2 = fn.price(settlement, maturity, rate, yield2, redemption, frequency, basis) + yieldN.Number = (yield2.Number - yield1.Number) * 0.5 + } else { + if pr.Number < priceN.Number { + yield1 = yieldN + price1 = priceN + } else { + yield2 = yieldN + price2 = priceN + } + yieldN.Number = yield2.Number - (yield2.Number-yield1.Number)*((pr.Number-price2.Number)/(price1.Number-price2.Number)) + } + } + return yieldN +} + +// YIELD function calculates the Yield of a security that pays periodic +// interest. The syntax of the function is: +// +// YIELD(settlement,maturity,rate,pr,redemption,frequency,[basis]) +// +func (fn *formulaFuncs) YIELD(argsList *list.List) formulaArg { + if argsList.Len() != 6 && argsList.Len() != 7 { + return newErrorFormulaArg(formulaErrorVALUE, "YIELD requires 6 or 7 arguments") + } + args := fn.prepareDataValueArgs(2, argsList) + if args.Type != ArgList { + return args + } + settlement, maturity := args.List[0], args.List[1] + rate := argsList.Front().Next().Next().Value.(formulaArg).ToNumber() + if rate.Type != ArgNumber { + return rate + } + if rate.Number < 0 { + return newErrorFormulaArg(formulaErrorNUM, "PRICE requires rate >= 0") + } + pr := argsList.Front().Next().Next().Next().Value.(formulaArg).ToNumber() + if pr.Type != ArgNumber { + return pr + } + if pr.Number <= 0 { + return newErrorFormulaArg(formulaErrorNUM, "PRICE requires pr > 0") + } + redemption := argsList.Front().Next().Next().Next().Next().Value.(formulaArg).ToNumber() + if redemption.Type != ArgNumber { + return redemption + } + if redemption.Number < 0 { + return newErrorFormulaArg(formulaErrorNUM, "PRICE requires redemption >= 0") + } + frequency := argsList.Front().Next().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() == 7 { + if basis = argsList.Back().Value.(formulaArg).ToNumber(); basis.Type != ArgNumber { + return newErrorFormulaArg(formulaErrorNUM, formulaErrorNUM) + } + } + return fn.yield(settlement, maturity, rate, pr, redemption, frequency, basis) +} + // YIELDDISC function calculates the annual yield of a discounted security. // The syntax of the function is: // diff --git a/calc_test.go b/calc_test.go index 880c40a..c18176f 100644 --- a/calc_test.go +++ b/calc_test.go @@ -1508,6 +1508,7 @@ func TestCalcCellValue(t *testing.T) { "=PRICE(\"04/01/2012\",\"02/01/2020\",12%,10%,100,2)": "110.65510517844305", "=PRICE(\"04/01/2012\",\"02/01/2020\",12%,10%,100,2,4)": "110.65510517844305", "=PRICE(\"04/01/2012\",\"03/31/2020\",12%,10%,100,2)": "110.83448359321572", + "=PRICE(\"01/01/2010\",\"06/30/2010\",0.5,1,1,1,4)": "8.924190888476605", // PPMT "=PPMT(0.05/12,2,60,50000)": "-738.2918003208238", "=PPMT(0.035/4,2,8,0,5000,1)": "-606.1094824182949", @@ -1552,6 +1553,12 @@ func TestCalcCellValue(t *testing.T) { "=VDB(24000,3000,10,0.1,1,1)": "2138.3999999999996", "=VDB(24000,3000,100,50,100,1)": "10377.294418465235", "=VDB(24000,3000,100,50,100,2)": "5740.072322090805", + // YIELD + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4)": "0.0975631546829798", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,4)": "0.0976269355643988", + "=YIELD(\"01/01/2010\",\"06/30/2010\",0.5,1,1,1,4)": "1.91285866099894", + "=YIELD(\"01/01/2010\",\"06/30/2010\",0,1,1,1,4)": "0", + "=YIELD(\"01/01/2010\",\"01/02/2020\",100,68.15518653988686,1,1,1)": "64", // YIELDDISC "=YIELDDISC(\"01/01/2017\",\"06/30/2017\",97,100)": "0.0622012325059031", "=YIELDDISC(\"01/01/2017\",\"06/30/2017\",97,100,0)": "0.0622012325059031", @@ -3124,6 +3131,21 @@ func TestCalcCellValue(t *testing.T) { "=VDB(10000,1000,5,0,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", "=VDB(10000,1000,5,0,1,\"\")": "#NUM!", "=VDB(10000,1000,5,0,1,0.2,\"\")": "#NUM!", + // YIELD + "=YIELD()": "YIELD requires 6 or 7 arguments", + "=YIELD(\"\",\"06/30/2015\",10%,101,100,4)": "#VALUE!", + "=YIELD(\"01/01/2010\",\"\",10%,101,100,4)": "#VALUE!", + "=YIELD(\"01/01/2010\",\"06/30/2015\",\"\",101,100,4)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,\"\",100,4)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,\"\",4)": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,\"\")": "strconv.ParseFloat: parsing \"\": invalid syntax", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,\"\")": "#NUM!", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,3)": "#NUM!", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4,5)": "invalid basis", + "=YIELD(\"01/01/2010\",\"06/30/2015\",-1,101,100,4)": "PRICE requires rate >= 0", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,0,100,4)": "PRICE requires pr > 0", + "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,-1,4)": "PRICE requires redemption >= 0", + // "=YIELD(\"01/01/2010\",\"06/30/2015\",10%,101,100,4)": "PRICE requires rate >= 0", // YIELDDISC "=YIELDDISC()": "YIELDDISC requires 4 or 5 arguments", "=YIELDDISC(\"\",\"06/30/2017\",97,100,0)": "#VALUE!", diff --git a/merge.go b/merge.go index 1cd8acd..2d699a2 100644 --- a/merge.go +++ b/merge.go @@ -269,15 +269,15 @@ func (m *MergeCell) GetCellValue() string { return (*m)[1] } -// GetStartAxis returns the merge start axis. -// example: "C2" +// GetStartAxis returns the top left cell coordinates of merged range, for +// example: "C2". func (m *MergeCell) GetStartAxis() string { axis := strings.Split((*m)[0], ":") return axis[0] } -// GetEndAxis returns the merge end axis. -// example: "D4" +// GetEndAxis returns the bottom right cell coordinates of merged range, for +// example: "D4". func (m *MergeCell) GetEndAxis() string { axis := strings.Split((*m)[0], ":") return axis[1] diff --git a/styles.go b/styles.go index e32fb78..7f76377 100644 --- a/styles.go +++ b/styles.go @@ -2151,8 +2151,8 @@ func (f *File) NewConditionalStyle(style string) (int, error) { return s.Dxfs.Count - 1, nil } -// GetDefaultFont provides the default font name currently set in the workbook -// Documents generated by excelize start with Calibri. +// GetDefaultFont provides the default font name currently set in the +// workbook. The spreadsheet generated by excelize default font is Calibri. func (f *File) GetDefaultFont() string { font := f.readDefaultFont() return *font.Name.Val