C# XIRR计算

C# XIRR计算,c#,algorithm,math,worksheet-function,C#,Algorithm,Math,Worksheet Function,如何使用C#计算Excel的XIRR函数?根据openoffice文档(公式与Excel中相同),您需要在以下f(XIRR)方程中求解XIRR变量: 您可以通过以下方式计算xirr值: 计算上述函数的导数->f'(xirr) 在拥有f(xirr)和f'(xirr)之后,您可以使用迭代-著名公式-> 编辑 我有一点时间,这里是用于XIRR计算的完整C#代码: class xirr { public const double tol = 0.001; pub

如何使用C#计算Excel的
XIRR
函数?

根据openoffice文档(公式与Excel中相同),您需要在以下f(XIRR)方程中求解XIRR变量:

您可以通过以下方式计算xirr值:

  • 计算上述函数的导数->f'(xirr)
  • 在拥有
    f(xirr)
    f'(xirr)
    之后,您可以使用迭代-著名公式->
  • 编辑
    我有一点时间,这里是用于XIRR计算的完整C#代码:

    class xirr
        {
            public const double tol = 0.001;
            public delegate double fx(double x);
    
            public static fx composeFunctions(fx f1, fx f2) {
                return (double x) => f1(x) + f2(x);
            }
    
            public static fx f_xirr(double p, double dt, double dt0) {
                return (double x) => p*Math.Pow((1.0+x),((dt0-dt)/365.0));
            }
    
            public static fx df_xirr(double p, double dt, double dt0) {
                return (double x) => (1.0/365.0)*(dt0-dt)*p*Math.Pow((x+1.0),(((dt0-dt)/365.0)-1.0));
            }
    
            public static fx total_f_xirr(double[] payments, double[] days) {
                fx resf = (double x) => 0.0;
    
                for (int i = 0; i < payments.Length; i++) {
                    resf = composeFunctions(resf,f_xirr(payments[i],days[i],days[0]));
                }
    
                return resf;
            }
    
            public static fx total_df_xirr(double[] payments, double[] days) {
                fx resf = (double x) => 0.0;
    
                for (int i = 0; i < payments.Length; i++) {
                    resf = composeFunctions(resf,df_xirr(payments[i],days[i],days[0]));
                }
    
                return resf;
            }
    
            public static double Newtons_method(double guess, fx f, fx df) {
                double x0 = guess;
                double x1 = 0.0;
                double err = 1e+100;
    
                while (err > tol) {
                    x1 = x0 - f(x0)/df(x0);
                    err = Math.Abs(x1-x0);
                    x0 = x1;
                }
    
                return x0;
            }
    
            public static void Main (string[] args)
            {
                double[] payments = {-6800,1000,2000,4000}; // payments
                double[] days = {01,08,16,25}; // days of payment (as day of year)
                double xirr = Newtons_method(0.1,
                                             total_f_xirr(payments,days),
                                             total_df_xirr(payments,days));
    
                Console.WriteLine("XIRR value is {0}", xirr);
            }
        }
    
    xirr类
    {
    公共常数双tol=0.001;
    公共代表双外汇(双x);
    公共静态外汇组合功能(外汇f1,外汇f2){
    返回(双x)=>f1(x)+f2(x);
    }
    公共静态外汇f_xirr(双p,双dt,双dt0){
    返回(双x)=>p*数学功率((1.0+x),((dt0 dt)/365.0));
    }
    公共静态外汇df_xirr(双p、双dt、双dt0){
    返回值(双x)=>(1.0/365.0)*(dt0-dt)*p*数学功率((x+1.0),((dt0-dt)/365.0)-1.0);
    }
    公共静态外汇总额(双倍[]付款,双倍[]天){
    fx resf=(双x)=>0.0;
    对于(int i=0;i0.0;
    对于(int i=0;itol){
    x1=x0-f(x0)/df(x0);
    误差=数学绝对值(x1-x0);
    x0=x1;
    }
    返回x0;
    }
    公共静态void Main(字符串[]args)
    {
    double[]付款={-680100020004000};//付款
    double[]天={01,08,16,25};//付款天数(作为一年中的一天)
    双xirr=牛顿法(0.1,
    总额(付款,天数),
    总额(付款,天数);
    WriteLine(“XIRR值为{0}”,XIRR);
    }
    }
    
    顺便说一句,请记住,由于公式和/或牛顿法的限制,并非所有付款都会产生有效的XIRR

    干杯

    我从牛顿的解开始,但最终一些新的情况导致牛顿的方法失败。我创建了一个“智能”版本,当牛顿法失败时,它使用二分法(较慢)

    请注意我用于此解决方案的多个源的内联引用

    最后,您将无法在Excel中重现这些场景,因为Excel本身使用牛顿方法。有关此问题的有趣讨论,请参阅

    使用制度; 使用System.Collections.Generic; 使用System.Linq

    //见以下条款: // // // //基于Excel文档的默认值 //

    名称空间Xirr { 公共课程 { private const Double DaysPerYear=365.0; 私有常量int最大迭代次数=100; 私有常量双默认容差=1E-6; private const double DefaultGuess=0.1

    private static readonly Func newtonsmethod=
    cf=>newtonMethodImplementation(cf,Xnpv,XnpvPrime);
    私有静态只读函数对分方法=
    cf=>BisectionMethodImplementation(cf,Xnpv);
    公共静态void Main(字符串[]args)
    {
    运行场景(新[]
    {
    //这种情况在牛顿的情况下失败了,但在较慢的二分法中成功了
    新现金项目(新日期时间(2012年6月1日),0.01),
    新现金项目(新日期时间(2012年7月23日),3042626.18),
    新的现金项目(新的日期时间(2012年11月7日),-491356.62),
    新现金项目(新日期时间(2012年11月30日),631579.92),
    新现金项目(新日期时间(2012年12月1日),19769.5),
    新现金项目(新日期时间(2013年1月16日),1551771.47),
    新现金项目(新日期时间(2013年2月8日),-304595),
    新现金项目(新日期时间(2013年3月26日),3880609.64),
    新现金项目(新日期时间(2013年3月31日),-4331949.61)
    });
    运行场景(新[]
    {
    新的现金项目(新的日期时间(2001年5月1日),10000),
    新的现金项目(新的日期时间(2002年3月1日),2000年),
    新的现金项目(新的日期时间(2002年5月1日),-5500),
    新的现金项目(新的日期时间(2002,9,1),3000),
    新的现金项目(新的日期时间(2003,2,1),3500),
    新现金项目(新日期时间(2003年5月1日),-15000)
    });
    }
    私有静态无效运行场景(IEnumerable现金流)
    {
    尝试
    {
    尝试
    {
    var结果=CalcXirr(现金流,新方法);
    WriteLine(“XIRR[Newton's]值为{0}”,result);
    }
    捕获(无效操作异常)
    {
    //失败:尝试其他算法
    var结果=CalcXirr(现金流,平分法);
    WriteLine(“XIRR[Bisection](Newton's failed)值为{0}”,result);
    }
    }
    捕获(e)
    {
    控制台写入线(e.Message);
    }
    捕获(InvalidOperationException异常)
    {
    Console.WriteLine(异常消息);
    }
    }
    私有静态双计算内部收益率(IEnumerable cash flow,Func method)
    {
    如果(现金流计数(cf=>cf.金额>0)==0)
    using System;
    using System.Collections.Generic;
    using System.Linq;

    // See the following articles: // http://blogs.msdn.com/b/lucabol/archive/2007/12/17/bisection-based-xirr-implementation-in-c.aspx // http://www.codeproject.com/Articles/79541/Three-Methods-for-Root-finding-in-C // http://www.financialwebring.org/forum/viewtopic.php?t=105243&highlight=xirr // Default values based on Excel doc // http://office.microsoft.com/en-us/excel-help/xirr-function-HP010062387.aspx

    namespace Xirr { public class Program { private const Double DaysPerYear = 365.0; private const int MaxIterations = 100; private const double DefaultTolerance = 1E-6; private const double DefaultGuess = 0.1;

        private static readonly Func<IEnumerable<CashItem>, Double> NewthonsMethod =
            cf => NewtonsMethodImplementation(cf, Xnpv, XnpvPrime);
    
        private static readonly Func<IEnumerable<CashItem>, Double> BisectionMethod =
            cf => BisectionMethodImplementation(cf, Xnpv);
    
        public static void Main(string[] args)
        {
            RunScenario(new[]
                {
                    // this scenario fails with Newton's but succeeds with slower Bisection
                    new CashItem(new DateTime(2012, 6, 1), 0.01),
                    new CashItem(new DateTime(2012, 7, 23), 3042626.18),
                    new CashItem(new DateTime(2012, 11, 7), -491356.62),
                    new CashItem(new DateTime(2012, 11, 30), 631579.92),
                    new CashItem(new DateTime(2012, 12, 1), 19769.5),
                    new CashItem(new DateTime(2013, 1, 16), 1551771.47),
                    new CashItem(new DateTime(2013, 2, 8), -304595),
                    new CashItem(new DateTime(2013, 3, 26), 3880609.64),
                    new CashItem(new DateTime(2013, 3, 31), -4331949.61)
                });
            RunScenario(new[]
                {
                    new CashItem(new DateTime(2001, 5, 1), 10000),
                    new CashItem(new DateTime(2002, 3, 1), 2000),
                    new CashItem(new DateTime(2002, 5, 1), -5500),
                    new CashItem(new DateTime(2002, 9, 1), 3000),
                    new CashItem(new DateTime(2003, 2, 1), 3500),
                    new CashItem(new DateTime(2003, 5, 1), -15000)
                });
        }
    
        private static void RunScenario(IEnumerable<CashItem> cashFlow)
        {
            try
            {
                try
                {
                    var result = CalcXirr(cashFlow, NewthonsMethod);
                    Console.WriteLine("XIRR [Newton's] value is {0}", result);
                }
                catch (InvalidOperationException)
                {
                    // Failed: try another algorithm
                    var result = CalcXirr(cashFlow, BisectionMethod);
                    Console.WriteLine("XIRR [Bisection] (Newton's failed) value is {0}", result);
                }
            }
            catch (ArgumentException e)
            {
                Console.WriteLine(e.Message);
            }
            catch (InvalidOperationException exception)
            {
                Console.WriteLine(exception.Message);
            }
        }
    
        private static double CalcXirr(IEnumerable<CashItem> cashFlow, Func<IEnumerable<CashItem>, double> method)
        {
            if (cashFlow.Count(cf => cf.Amount > 0) == 0)
                throw new ArgumentException("Add at least one positive item");
    
            if (cashFlow.Count(c => c.Amount < 0) == 0)
                throw new ArgumentException("Add at least one negative item");
    
            var result = method(cashFlow);
    
            if (Double.IsInfinity(result))
                throw new InvalidOperationException("Could not calculate: Infinity");
    
            if (Double.IsNaN(result))
                throw new InvalidOperationException("Could not calculate: Not a number");
    
            return result;
        }
    
        private static Double NewtonsMethodImplementation(IEnumerable<CashItem> cashFlow,
                                                          Func<IEnumerable<CashItem>, Double, Double> f,
                                                          Func<IEnumerable<CashItem>, Double, Double> df,
                                                          Double guess = DefaultGuess,
                                                          Double tolerance = DefaultTolerance,
                                                          int maxIterations = MaxIterations)
        {
            var x0 = guess;
            var i = 0;
            Double error;
            do
            {
                var dfx0 = df(cashFlow, x0);
                if (Math.Abs(dfx0 - 0) < Double.Epsilon)
                    throw new InvalidOperationException("Could not calculate: No solution found. df(x) = 0");
    
                var fx0 = f(cashFlow, x0);
                var x1 = x0 - fx0/dfx0;
                error = Math.Abs(x1 - x0);
    
                x0 = x1;
            } while (error > tolerance && ++i < maxIterations);
            if (i == maxIterations)
                throw new InvalidOperationException("Could not calculate: No solution found. Max iterations reached.");
    
            return x0;
        }
    
        internal static Double BisectionMethodImplementation(IEnumerable<CashItem> cashFlow,
                                                             Func<IEnumerable<CashItem>, Double, Double> f,
                                                             Double tolerance = DefaultTolerance,
                                                             int maxIterations = MaxIterations)
        {
            // From "Applied Numerical Analysis" by Gerald
            var brackets = Brackets.Find(Xnpv, cashFlow);
            if (Math.Abs(brackets.First - brackets.Second) < Double.Epsilon)
                throw new ArgumentException("Could not calculate: bracket failed");
    
            Double f3;
            Double result;
            var x1 = brackets.First;
            var x2 = brackets.Second;
    
            var i = 0;
            do
            {
                var f1 = f(cashFlow, x1);
                var f2 = f(cashFlow, x2);
    
                if (Math.Abs(f1) < Double.Epsilon && Math.Abs(f2) < Double.Epsilon)
                    throw new InvalidOperationException("Could not calculate: No solution found");
    
                if (f1*f2 > 0)
                    throw new ArgumentException("Could not calculate: bracket failed for x1, x2");
    
                result = (x1 + x2)/2;
                f3 = f(cashFlow, result);
    
                if (f3*f1 < 0)
                    x2 = result;
                else
                    x1 = result;
            } while (Math.Abs(x1 - x2)/2 > tolerance && Math.Abs(f3) > Double.Epsilon && ++i < maxIterations);
    
            if (i == maxIterations)
                throw new InvalidOperationException("Could not calculate: No solution found");
    
            return result;
        }
    
        private static Double Xnpv(IEnumerable<CashItem> cashFlow, Double rate)
        {
            if (rate <= -1)
                rate = -1 + 1E-10; // Very funky ... Better check what an IRR <= -100% means
    
            var startDate = cashFlow.OrderBy(i => i.Date).First().Date;
            return
                (from item in cashFlow
                 let days = -(item.Date - startDate).Days
                 select item.Amount*Math.Pow(1 + rate, days/DaysPerYear)).Sum();
        }
    
        private static Double XnpvPrime(IEnumerable<CashItem> cashFlow, Double rate)
        {
            var startDate = cashFlow.OrderBy(i => i.Date).First().Date;
            return (from item in cashFlow
                    let daysRatio = -(item.Date - startDate).Days/DaysPerYear
                    select item.Amount*daysRatio*Math.Pow(1.0 + rate, daysRatio - 1)).Sum();
        }
    
        public struct Brackets
        {
            public readonly Double First;
            public readonly Double Second;
    
            public Brackets(Double first, Double second)
            {
                First = first;
                Second = second;
            }
    
            internal static Brackets Find(Func<IEnumerable<CashItem>, Double, Double> f,
                                          IEnumerable<CashItem> cashFlow,
                                          Double guess = DefaultGuess,
                                          int maxIterations = MaxIterations)
            {
                const Double bracketStep = 0.5;
                var leftBracket = guess - bracketStep;
                var rightBracket = guess + bracketStep;
                var i = 0;
                while (f(cashFlow, leftBracket)*f(cashFlow, rightBracket) > 0 && i++ < maxIterations)
                {
                    leftBracket -= bracketStep;
                    rightBracket += bracketStep;
                }
    
                return i >= maxIterations
                           ? new Brackets(0, 0)
                           : new Brackets(leftBracket, rightBracket);
            }
        }
    
        public struct CashItem
        {
            public DateTime Date;
            public Double Amount;
    
            public CashItem(DateTime date, Double amount)
            {
                Date = date;
                Amount = amount;
            }
        }
    }
    
        public static double Xirr(IList<double> values, IList<DateTime> dates)
        {
            var xlApp = new Application();
    
            var datesAsDoubles = new List<double>();
            foreach (var date in dates)
            {
                var totalDays = (date - DateTime.MinValue).TotalDays;
                datesAsDoubles.Add(totalDays);
            }
    
            var valuesArray = values.ToArray();
            var datesArray = datesAsDoubles.ToArray();
    
            return xlApp.WorksheetFunction.Xirr(valuesArray, datesArray);
        }
    
    using Excel.FinancialFunctions;
    
    namespace ExcelXirr
    {
        class Program
        {
            static void Main(string[] args)
            {
                List<double> valList =new List<double>();
                valList.Add(4166.67);
                valList.Add(-4166.67);
                valList.Add(-4166.67);
                valList.Add(-4166.67);
                List<DateTime> dtList = new List<DateTime>();
                dtList.Add(new DateTime(2014, 9, 1));
                dtList.Add(new DateTime(2014, 10, 1));
                dtList.Add(new DateTime(2014, 11, 1));
                dtList.Add(new DateTime(2014, 12, 1));
                double result = Financial.XIrr(valList, dtList);
                Console.WriteLine(result);
                Console.ReadLine();
            }
        }
    }