如何使用C#計算Excel的XIRR
函數?XIRR計算
XIRR計算
回答
根據XIRR function OpenOffice的文檔(式是相同的Excel),則需要在以下F(XIRR)來求解XIRR可變方程:
可以計算通過XIRR值:
- 計算上述功能的衍生物 - >F「(XIRR)
- 具有
f(xirr)
後和f'(xirr)
可以解決˚F通過使用迭代Newton's method或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!
喝彩!
我從0x69的解決方案開始,但最終導致一些新的情況導致牛頓方法失敗。我創建了一個「聰明」的版本,當牛頓失敗時使用平分法(較慢)。
請注意我用於此解決方案的多個源的內聯引用。
最後,您不能在Excel中重現這些場景中的一些場景,因爲Excel本身使用牛頓的方法。有關這方面的有趣討論,請參閱XIRR, eh?。
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; } } }
}
只是聲明我在使用您的代碼,並且它按預期工作。 – BrunoSalvino 2011-12-14 18:35:57
對於這些代碼的採用者來說,只要謹慎一點,它就可以很好地工作,但是您可能會遇到一個奇怪的邊界情況,您會進入永無止境的循環,本質上是死鎖。爲了防止這種情況的發生,我強烈建議在NewtonsMethod方法中增加一個100或者任意數字的最大迭代次數。像這樣:while(err> TOLERANCE && i <100),在每次迭代時增加i。我得到的價值依然是Excel給我的東西。這隻會發生(從我看到的情況),如果你收斂到無窮大,但不能很快到達那裏。 – dyslexicanaboko 2012-10-16 15:41:21
我發現另一個邊緣案例,這很重要,因爲Excel中的結果是不同的。 NewtonsMethod方法內部 - 在計算過程中,如果x0和x1是無窮大,則從該方法返回的結果是無窮大。原因是Infinity - Infinity = NaN,但設置err後x0取得x1的值,因此最後一個值爲Infinity。在Excel中,結果爲零!以下是我的數據集:值{-10000,10100,10000},日期{12/26/2010,12/26/2010,10/16/2012}。也許包括一個if語句尋找err == double.NaN,然後x0 = 0;我猶豫不決。 – dyslexicanaboko 2012-10-17 18:58:08
其他的答案顯示如何C#實現 XIRR,但如果只需要計算結果您可以在下面直接調用Excel的XIRR功能方式:
首先添加對Microsoft.Office.Interop.Excel的引用,然後使用以下方法:
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);
}
與其他答案中的C#代碼實現相比,這可能會相當慢(但是,如果性能不是問題,這是一個很好的演示!)。 – user700390 2016-06-01 15:01:39
- 1. 用Python計算XIRR
- 2. Excel中的XIRR計算
- 3. java程序來計算XIRR而不使用excel或任何其他庫
- 4. 使用XIRR函數的PHP財務類
- 5. javascript計算不計算
- 6. 計算使用CSS計算()
- 7. 算術計算
- 8. 統計計算
- 9. Python中,計算計算的狀態減慢計算本身
- 10. 用算法計算
- 11. 計算
- 12. 計算
- 13. 計算
- 14. 計算總計值
- 15. 控制檯計算器繼續計算
- 16. 在10.功率/計算/功率計算
- 17. 基於計算值的Django計算
- 18. VuexFire:如何合併計算:和()計算
- 19. 按位計算的計算器和Javascript
- 20. 運費計算器小姐計算
- 21. CSS計算與計算的變量
- 22. 計算兩次單獨計算
- 23. c# - 計算器 - 用浮點數計算
- 24. JQuery:計算插件不計算總價
- 25. 計算並加入MYSQL中的計算
- 26. 計算圖:提前計算值
- 27. Sharepoint計算列何時實際計算?
- 28. jQuery年齡計算不計算閏年
- 29. 更改計算器不計算便士
- 30. 計算計算複雜度(Big-O)
也許[這篇來自Microsoft的KB文章](http://support.microsoft.com/kb/214105/en-us)確實有幫助嗎? – 2011-03-03 13:34:33
看到我的編輯 - 我已經添加完整的C#示例... – 2011-03-04 11:27:11
並且不要忘記標記答案爲接受,如果你覺得它解決了你的問題。 – 2011-03-04 11:34:55