在C#中模拟Excel的YearFrac

时间:2011-12-19 17:46:47

标签: c# excel

如何在C#应用程序中获得Excel YearFrac function的相同结果?

  

计算两个日期(start_date和end_date)之间的整天数表示的年份分数。使用YEARFRAC工作表函数来确定一年的利益或分配给特定条款的义务的比例。

4 个答案:

答案 0 :(得分:2)

这是一个很好的snippet

YearFrac功能的算法实际上非常复杂。也许this article可以为您提供更多详细信息。

答案 1 :(得分:1)

您可以直接使用Excel的功能来计算YearFrac。微软称you are not supposed to use it,但效果很好。如果您需要与Excel 100%兼容,这个解决方案很难被击败。您需要在项目中添加对Microsoft.Office.Interop.Excel的引用,以便编译此代码。

static void Main() {
    var excel = new Microsoft.Office.Interop.Excel.Application();
    Microsoft.Office.Interop.Excel.WorksheetFunction wsf = excel.WorksheetFunction;
    var start = new DateTime(1999, 11, 1);
    var end = new DateTime(1999, 1, 11);
    for (var basis = 0; basis != 5; basis++) {
        Console.WriteLine(wsf.YearFrac(start, end, basis));
    }
}

答案 2 :(得分:0)

YEARFRAC的签名是YEARFRAC(Date startDate,Date endDate,int convention)。计算YEARFRAC的方法取决于惯例。

对于约定= 2,YEARFRAC将使用ACT / 360方法计算YEARFRAC。可以在svn.finmath.net找到ACT / 360的实施,具体为DayCountConvention_ACT_360.java

对于约定= 3,YEARFRAC将使用ACT / 365方法计算YEARFRAC。可以在svn.finmath.net专门找到ACT / 365的实现 DayCountConvention_ACT_365.java

对于约定= 4,YEARFRAC将使用30E / 360方法计算YEARFRAC。可以在svn.finmath.net找到30E / 360的实现,特别是DayCountConvention_30E_360.java

对于惯例= 1,文件声称YEARFRAC是使用ACT / ACT惯例计算的。但是,有多个版本的ACT / ACT,我相信许多金融产品的标准是ACT / ACT ISDA。我发现YEARFRAC与ACT / ACT IDSA大会相差不多!可在DayCountConvention_ACT_ACT_ISDA.java

找到ACT / ACT IDSA的实施

我还没有检查过其他行为/动作版本,但我不会依赖YEARFRAC ACT / ACT的仿真,当时不清楚他们实施的是什么样的方法......

答案 3 :(得分:0)

我可以建议:

    public static double Yearfrac(DateTime startDate,DateTime endDate,DayCount daycount=DayCount.ActAct)
    {
        var nbDaysInPeriod = (double)(endDate - startDate).Days;

        switch(daycount)
        {
            case (DayCount.Act360):
                return nbDaysInPeriod / (double)360;
            case (DayCount.Act365):
                return nbDaysInPeriod / (double)365;
            case (DayCount.ActAct):
                return GetActAct(startDate,endDate);
            case (DayCount.Days360):
                var result = (endDate.Year - startDate.Year) * 360.0 + (endDate.Month - startDate.Month) * 30.0 + (Math.Min(endDate.Day, 30.0) - Math.Min(startDate.Day, 30.0));
                return result/360;
            default:
                return nbDaysInPeriod / (double)365;
        }
    }

    public static double GetActAct(DateTime startDate, DateTime endDate)
    {
        // Reproduce Excel Yearfrac as per http://www.dwheeler.com/yearfrac/excel-ooxml-yearfrac.pdf
        var nbDaysInPeriod = (double)(endDate - startDate).Days;
        if(startDate.Year==endDate.Year || (endDate.Year-1==startDate.Year&&(startDate.Month>endDate.Month||startDate.Month==endDate.Month&&(startDate.Day>=endDate.Day))))
        {
            var den =  365.0;
            if (startDate.Year == endDate.Year && DateTime.IsLeapYear(startDate.Year))
            {
                den++;
            }
            else
            {

                if (endDate.Day == 29 && endDate.Month == 2)
                {
                    den++;
                }
                else
                {
                    if (DateTime.IsLeapYear(startDate.Year))
                    {
                        var feb = new DateTime(startDate.Year, 2, 29);
                        if (startDate<=feb && feb<=endDate) den++;
                    }
                    else
                    {
                        if (DateTime.IsLeapYear(endDate.Year))
                        {
                            var feb = new DateTime(endDate.Year, 2, 29);
                            if (startDate <= feb && feb <= endDate) den++;
                        }
                    }
                }
            }
        }
        else
        {
            var nbYears = endDate.Year - startDate.Year+1;
            var den = nbYears * 365.0;
            for (var i=0;i<nbYears;i++)
            {
                if (DateTime.IsLeapYear(startDate.Year + i)) den++;
            }
            den /= nbYears;
            return nbDaysInPeriod / den;
        }
        return nbDaysInPeriod / 365.0;
    }