C# XIRR计算
如何使用C#计算Excel的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
XIRR
函数?根据openoffice文档(公式与Excel中相同),您需要在以下f(XIRR)方程中求解XIRR变量:您可以通过以下方式计算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();
}
}
}