|
|
@ -350,6 +350,9 @@ type formulaFuncs struct {
|
|
|
|
// COUNT
|
|
|
|
// COUNT
|
|
|
|
// COUNTA
|
|
|
|
// COUNTA
|
|
|
|
// COUNTBLANK
|
|
|
|
// COUNTBLANK
|
|
|
|
|
|
|
|
// COUPDAYBS
|
|
|
|
|
|
|
|
// COUPDAYS
|
|
|
|
|
|
|
|
// COUPDAYSNC
|
|
|
|
// COUPNCD
|
|
|
|
// COUPNCD
|
|
|
|
// COUPNUM
|
|
|
|
// COUPNUM
|
|
|
|
// COUPPCD
|
|
|
|
// COUPPCD
|
|
|
@ -9642,17 +9645,23 @@ func (fn *formulaFuncs) prepareCouponArgs(name string, argsList *list.List) form
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 or 4 arguments", name))
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, fmt.Sprintf("%s requires 3 or 4 arguments", name))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
args := list.New().Init()
|
|
|
|
args := list.New().Init()
|
|
|
|
|
|
|
|
settlement := argsList.Front().Value.(formulaArg).ToNumber()
|
|
|
|
|
|
|
|
if settlement.Type != ArgNumber {
|
|
|
|
args.PushBack(argsList.Front().Value.(formulaArg))
|
|
|
|
args.PushBack(argsList.Front().Value.(formulaArg))
|
|
|
|
settlement := fn.DATEVALUE(args)
|
|
|
|
settlement = fn.DATEVALUE(args)
|
|
|
|
if settlement.Type != ArgNumber {
|
|
|
|
if settlement.Type != ArgNumber {
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
maturity := argsList.Front().Next().Value.(formulaArg).ToNumber()
|
|
|
|
|
|
|
|
if maturity.Type != ArgNumber {
|
|
|
|
args.Init()
|
|
|
|
args.Init()
|
|
|
|
args.PushBack(argsList.Front().Next().Value.(formulaArg))
|
|
|
|
args.PushBack(argsList.Front().Next().Value.(formulaArg))
|
|
|
|
maturity := fn.DATEVALUE(args)
|
|
|
|
maturity = fn.DATEVALUE(args)
|
|
|
|
if maturity.Type != ArgNumber {
|
|
|
|
if maturity.Type != ArgNumber {
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
|
|
|
return newErrorFormulaArg(formulaErrorVALUE, formulaErrorVALUE)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
if settlement.Number >= maturity.Number {
|
|
|
|
if settlement.Number >= maturity.Number {
|
|
|
|
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name))
|
|
|
|
return newErrorFormulaArg(formulaErrorNUM, fmt.Sprintf("%s requires maturity > settlement", name))
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -9672,6 +9681,126 @@ func (fn *formulaFuncs) prepareCouponArgs(name string, argsList *list.List) form
|
|
|
|
return newListFormulaArg([]formulaArg{settlement, maturity, frequency, basis})
|
|
|
|
return newListFormulaArg([]formulaArg{settlement, maturity, frequency, basis})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// is30BasisMethod determine if the financial day count basis rules is 30/360
|
|
|
|
|
|
|
|
// methods.
|
|
|
|
|
|
|
|
func is30BasisMethod(basis int) bool {
|
|
|
|
|
|
|
|
return basis == 0 || basis == 4
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// getDaysInMonthRange return the day by given year, month range and day count
|
|
|
|
|
|
|
|
// basis.
|
|
|
|
|
|
|
|
func getDaysInMonthRange(year, fromMonth, toMonth, basis int) int {
|
|
|
|
|
|
|
|
if fromMonth > toMonth {
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (toMonth - fromMonth + 1) * 30
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// getDayOnBasis returns the day by given date and day count basis.
|
|
|
|
|
|
|
|
func getDayOnBasis(y, m, d, basis int) int {
|
|
|
|
|
|
|
|
if !is30BasisMethod(basis) {
|
|
|
|
|
|
|
|
return d
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
day := d
|
|
|
|
|
|
|
|
dim := getDaysInMonth(y, m)
|
|
|
|
|
|
|
|
if day > 30 || d >= dim || day >= dim {
|
|
|
|
|
|
|
|
day = 30
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return day
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// coupdays returns the number of days that base on date range and the day
|
|
|
|
|
|
|
|
// count basis to be used.
|
|
|
|
|
|
|
|
func coupdays(from, to time.Time, basis int) float64 {
|
|
|
|
|
|
|
|
days := 0
|
|
|
|
|
|
|
|
fromY, fromM, fromD := from.Date()
|
|
|
|
|
|
|
|
toY, toM, toD := to.Date()
|
|
|
|
|
|
|
|
fromDay, toDay := getDayOnBasis(fromY, int(fromM), fromD, basis), getDayOnBasis(toY, int(toM), toD, basis)
|
|
|
|
|
|
|
|
if !is30BasisMethod(basis) {
|
|
|
|
|
|
|
|
return (daysBetween(excelMinTime1900.Unix(), makeDate(toY, toM, toDay)) + 1) - (daysBetween(excelMinTime1900.Unix(), makeDate(fromY, fromM, fromDay)) + 1)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if basis == 0 {
|
|
|
|
|
|
|
|
if (int(fromM) == 2 || fromDay < 30) && toD == 31 {
|
|
|
|
|
|
|
|
toDay = 31
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
if int(fromM) == 2 && fromDay == 30 {
|
|
|
|
|
|
|
|
fromDay = getDaysInMonth(fromY, 2)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if int(toM) == 2 && toDay == 30 {
|
|
|
|
|
|
|
|
toDay = getDaysInMonth(toY, 2)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if fromY < toY || (fromY == toY && int(fromM) < int(toM)) {
|
|
|
|
|
|
|
|
days = 30 - fromDay + 1
|
|
|
|
|
|
|
|
fromD = 1
|
|
|
|
|
|
|
|
fromDay = 1
|
|
|
|
|
|
|
|
date := time.Date(fromY, fromM, fromD, 0, 0, 0, 0, time.UTC).AddDate(0, 1, 0)
|
|
|
|
|
|
|
|
if date.Year() < toY {
|
|
|
|
|
|
|
|
days += getDaysInMonthRange(date.Year(), int(date.Month()), 12, basis)
|
|
|
|
|
|
|
|
date = date.AddDate(0, 13-int(date.Month()), 0)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
days += getDaysInMonthRange(toY, int(date.Month()), int(toM)-1, basis)
|
|
|
|
|
|
|
|
date = date.AddDate(0, int(toM)-int(date.Month()), 0)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
days += toDay - fromDay
|
|
|
|
|
|
|
|
if days > 0 {
|
|
|
|
|
|
|
|
return float64(days)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// COUPDAYBS function calculates the number of days from the beginning of a
|
|
|
|
|
|
|
|
// coupon's period to the settlement date. The syntax of the function is:
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// COUPDAYBS(settlement,maturity,frequency,[basis])
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
func (fn *formulaFuncs) COUPDAYBS(argsList *list.List) formulaArg {
|
|
|
|
|
|
|
|
args := fn.prepareCouponArgs("COUPDAYBS", argsList)
|
|
|
|
|
|
|
|
if args.Type != ArgList {
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
settlement := timeFromExcelTime(args.List[0].Number, false)
|
|
|
|
|
|
|
|
pcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false)
|
|
|
|
|
|
|
|
return newNumberFormulaArg(coupdays(pcd, settlement, int(args.List[3].Number)))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// COUPDAYS function calculates the number of days in a coupon period that
|
|
|
|
|
|
|
|
// contains the settlement date. The syntax of the function is:
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// COUPDAYS(settlement,maturity,frequency,[basis])
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
func (fn *formulaFuncs) COUPDAYS(argsList *list.List) formulaArg {
|
|
|
|
|
|
|
|
args := fn.prepareCouponArgs("COUPDAYS", argsList)
|
|
|
|
|
|
|
|
if args.Type != ArgList {
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
freq := args.List[2].Number
|
|
|
|
|
|
|
|
basis := int(args.List[3].Number)
|
|
|
|
|
|
|
|
if basis == 1 {
|
|
|
|
|
|
|
|
pcd := timeFromExcelTime(fn.COUPPCD(argsList).Number, false)
|
|
|
|
|
|
|
|
next := pcd.AddDate(0, 12/int(freq), 0)
|
|
|
|
|
|
|
|
return newNumberFormulaArg(coupdays(pcd, next, basis))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return newNumberFormulaArg(float64(getYearDays(0, basis)) / freq)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// COUPDAYSNC function calculates the number of days from the settlement date
|
|
|
|
|
|
|
|
// to the next coupon date. The syntax of the function is:
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
// COUPDAYSNC(settlement,maturity,frequency,[basis])
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
func (fn *formulaFuncs) COUPDAYSNC(argsList *list.List) formulaArg {
|
|
|
|
|
|
|
|
args := fn.prepareCouponArgs("COUPDAYSNC", argsList)
|
|
|
|
|
|
|
|
if args.Type != ArgList {
|
|
|
|
|
|
|
|
return args
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
settlement := timeFromExcelTime(args.List[0].Number, false)
|
|
|
|
|
|
|
|
basis := int(args.List[3].Number)
|
|
|
|
|
|
|
|
ncd := timeFromExcelTime(fn.COUPNCD(argsList).Number, false)
|
|
|
|
|
|
|
|
return newNumberFormulaArg(coupdays(settlement, ncd, basis))
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// COUPNCD function calculates the number of coupons payable, between a
|
|
|
|
// COUPNCD function calculates the number of coupons payable, between a
|
|
|
|
// security's settlement date and maturity date, rounded up to the nearest
|
|
|
|
// security's settlement date and maturity date, rounded up to the nearest
|
|
|
|
// whole coupon. The syntax of the function is:
|
|
|
|
// whole coupon. The syntax of the function is:
|
|
|
|