作为
Formula.js项目的一部分,我正在尝试重新实现Excel的
ACCRINT功能(在JavaScript中,但语言无关紧要).我一直试图找到一个适当的描述它应该如何工作(特别是关于first_interest参数),但找不到任何东西.
有趣的是,Excel,Google Spreadsheets,Apple Numbers,Gnumeric和OpenOffice在实现它的方式上都存在分歧,尽管所有三个主要版本的Excel(Win,Mac,Web)似乎都互相认同.在blog post上可以找到更多的上下文.
可以在here找到数十个测试用例和我当前(有缺陷的)实现.
任何帮助将不胜感激!
更新:很明显,这个问题与day count convention没有关系,我们使用David Wheeler的pseudocode YEARFRAC实现了这个问题,它本身已经过3200万次测试验证,涵盖了所有五个基本选项.问题来自first_interest参数,似乎没有人真正理解.据我们所知,许多替代电子表格都忽略了这个参数,包括OpenOffice(它在源代码中被注释掉).而这个参数确实表现得很奇怪.如果您使用Excel并更改其值,您将看到它将更改ACCRINT函数给出的结果,但是看起来很混乱.尝试将first_interest日期改变一整个世纪,你会看到应计利息的变化,但不是很多.我真的无法理解这一点.如果有人能,我全都耳朵……
解决方法
这是在PHP(我认为):从
this page
/** * ACCRINT * * Returns the discount rate for a security. * * @param mixed issue The security's issue date. * @param mixed firstinter The security's first interest date. * @param mixed settlement The security's settlement date. * @param float rate The security's annual coupon rate. * @param float par The security's par value. * @param int basis The type of day count to use. * 0 or omitted US (NASD) 30/360 * 1 Actual/actual * 2 Actual/360 * 3 Actual/365 * 4 European 30/360 * @return float */ public static function ACCRINT($issue,$firstinter,$settlement,$rate,$par=1000,$frequency=1,$basis=0) { $issue = self::flattenSingleValue($issue); $firstinter = self::flattenSingleValue($firstinter); $settlement = self::flattenSingleValue($settlement); $rate = (float) self::flattenSingleValue($rate); $par = (is_null($par)) ? 1000 : (float) self::flattenSingleValue($par); $frequency = (is_null($frequency)) ? 1 : (int) self::flattenSingleValue($frequency); $basis = (is_null($basis)) ? 0 : (int) self::flattenSingleValue($basis); // Validate if ((is_numeric($rate)) && (is_numeric($par))) { if (($rate <= 0) || ($par <= 0)) { return self::$_errorCodes['num']; } $daysBetweenIssueAndSettlement = self::YEARFRAC($issue,$basis); if (!is_numeric($daysBetweenIssueAndSettlement)) { return $daysBetweenIssueAndSettlement; } $daysPerYear = self::_daysPerYear(self::YEAR($issue),$basis); if (!is_numeric($daysPerYear)) { return $daysPerYear; } $daysBetweenIssueAndSettlement *= $daysPerYear; return $par * $rate * ($daysBetweenIssueAndSettlement / $daysPerYear); } return self::$_errorCodes['value']; } // function ACCRINT()