Home » excel » Emulating Excel's YearFrac in C#

Emulating Excel's YearFrac in C#

Posted by: admin May 14, 2020 Leave a comment

Questions:

How can I get the same results of the Excel YearFrac function in my C# application?

Calculates the fraction of the year represented by the number of whole days between two dates (the start_date and the end_date). Use the YEARFRAC worksheet function to identify the proportion of a whole year’s benefits or obligations to assign to a specific term.

How to&Answers:

Here is a good snippet.

The algorithm for the YearFrac function is in fact very complex. Maybe this article can provide you with more details.

Answer:

You can use Excel’s functionality directly to calculate YearFrac. Microsoft says you are not supposed to use it, but it works very well. If you need a 100% compatibility with Excel, this solution is hard to beat. You need to add to your project a reference to Microsoft.Office.Interop.Excel in order for this code to compile.

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));
    }
}

Answer:

The signature of YEARFRAC is YEARFRAC(Date startDate, Date endDate, int convention). The method to calculate the YEARFRAC depends on the convention.

For convention = 2, YEARFRAC will calculate the YEARFRAC using ACT/360 method. An implementation of the ACT/360 can be found at svn.finmath.net, specifically DayCountConvention_ACT_360.java

For convention = 3, YEARFRAC will calculate the YEARFRAC using ACT/365 method. An implementation of the ACT/365 can be found at svn.finmath.net, specifically
DayCountConvention_ACT_365.java

For convention = 4, YEARFRAC will calculate the YEARFRAC using 30E/360 method. An implementation of the 30E/360 can be found at svn.finmath.net, specifically DayCountConvention_30E_360.java

For convention = 1, the documentation claims that YEARFRAC is calculate using ACT/ACT convention. However, there are a multiple versions of ACT/ACT and I believe the standard for many financial products is ACT/ACT ISDA. I found that YEARFRAC differs by a small amount from the ACT/ACT IDSA convention! An implementation of the ACT/ACT IDSA can be found at DayCountConvention_ACT_ACT_ISDA.java

I haven’t checked the other act/act versions yet, but I would not rely on an emulation of YEARFRAC ACT/ACT, when it is not clear what kind of method they implement…

Answer:

May I suggest:

    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;
    }