diff --git a/.idea/.idea.EvoCalculator.Core/.idea/contentModel.xml b/.idea/.idea.EvoCalculator.Core/.idea/contentModel.xml index 8a34018..d174da7 100644 --- a/.idea/.idea.EvoCalculator.Core/.idea/contentModel.xml +++ b/.idea/.idea.EvoCalculator.Core/.idea/contentModel.xml @@ -38,6 +38,10 @@ + + + + @@ -47,12 +51,17 @@ - - - - - + + + + + + + + + + @@ -71,6 +80,12 @@ + + + + + + diff --git a/.idea/.idea.EvoCalculator.Core/.idea/workspace.xml b/.idea/.idea.EvoCalculator.Core/.idea/workspace.xml index 6d087df..1d9e1ef 100644 --- a/.idea/.idea.EvoCalculator.Core/.idea/workspace.xml +++ b/.idea/.idea.EvoCalculator.Core/.idea/workspace.xml @@ -14,16 +14,17 @@ - - - + + + + + + - - - - - + + + + + + @@ -103,14 +121,15 @@ - + + \ No newline at end of file diff --git a/EvoCalculator.Core.Calculation/EvoCalculator.Core.Calculation.csproj b/EvoCalculator.Core.Calculation/EvoCalculator.Core.Calculation.csproj index 8642d92..1b2a4e8 100644 --- a/EvoCalculator.Core.Calculation/EvoCalculator.Core.Calculation.csproj +++ b/EvoCalculator.Core.Calculation/EvoCalculator.Core.Calculation.csproj @@ -4,4 +4,8 @@ netcoreapp3.1 + + + + diff --git a/EvoCalculator.Core.Calculation/FinanceFormulas/XIRR.cs b/EvoCalculator.Core.Calculation/FinanceFormulas/XIRR.cs new file mode 100644 index 0000000..fac2e86 --- /dev/null +++ b/EvoCalculator.Core.Calculation/FinanceFormulas/XIRR.cs @@ -0,0 +1,73 @@ +using System; +using EvoCalculator.Core.Models.Calculation.Interfaces; +using EvoCalculator.Core.Models.Calculation.Models; + +namespace EvoCalculator.Core.Calculation.FinanceFormulas +{ + public class XIRR : IFinanceFormula + { + private FlowValue[] _values; + private double _guess = 0.1; + + public XIRR(FlowValue[] values) + { + _values = values; + } + + public XIRR(FlowValue[] values, double guess) + { + _values = values; + _guess = guess; + } + + public double GetResult() + { + var x1 = 0.0; + var x2 = _guess; + var f1 = new XNPV(_values, x1).GetResult(); + var f2 = new XNPV(_values, x2).GetResult(); + + for (var i = 0; i < 100; i++) + { + if (f1 * f2 < 0.0) break; + if (Math.Abs(f1) < Math.Abs(f2)) + { + x1 += 1.6 * (x1 - x2); + f1 = new XNPV(_values, x1).GetResult(); + } + else + { + x2 += 1.6 * (x2 - x1); + f2 = new XNPV(_values, x2).GetResult(); + } + } + + if (f1 * f2 > 0.0) return 0; + + var f = new XNPV(_values, x1).GetResult(); + var dx = 0.0; + var rtb = 0.0; + if (f < 0.0) + { + rtb = x1; + dx = x2 - x1; + } + else + { + rtb = x2; + dx = x1 - x2; + } + + for (var i = 0; i < 100; i++) + { + dx *= 0.5; + var xMid = rtb + dx; + var fMid = new XNPV(_values, xMid).GetResult(); + if (fMid <= 0.0) rtb = xMid; + if (Math.Abs(fMid) < 1.0e-6 || Math.Abs(dx) < 1.0e-6) return xMid; + } + + return 0; + } + } +} \ No newline at end of file diff --git a/EvoCalculator.Core.Calculation/FinanceFormulas/XNPV.cs b/EvoCalculator.Core.Calculation/FinanceFormulas/XNPV.cs new file mode 100644 index 0000000..98b225c --- /dev/null +++ b/EvoCalculator.Core.Calculation/FinanceFormulas/XNPV.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq; +using EvoCalculator.Core.Models.Calculation.Interfaces; +using EvoCalculator.Core.Models.Calculation.Models; + +namespace EvoCalculator.Core.Calculation.FinanceFormulas +{ + public class XNPV : IFinanceFormula + { + private FlowValue[] _values; + private double _rate; + + public XNPV(FlowValue[] values, double rate) + { + _values = values; + _rate = rate; + } + + public double GetResult() + { + var firstDate = _values[0].Date; + return _values.Sum(flowValue => + flowValue.Flow / Math.Pow(1 + _rate, (flowValue.Date - firstDate).TotalDays / 365)); + } + } +} \ No newline at end of file diff --git a/EvoCalculator.Core.Models/Calculation/Interfaces/IFinanceFormula.cs b/EvoCalculator.Core.Models/Calculation/Interfaces/IFinanceFormula.cs new file mode 100644 index 0000000..b4163b6 --- /dev/null +++ b/EvoCalculator.Core.Models/Calculation/Interfaces/IFinanceFormula.cs @@ -0,0 +1,7 @@ +namespace EvoCalculator.Core.Models.Calculation.Interfaces +{ + public interface IFinanceFormula + { + public T GetResult(); + } +} \ No newline at end of file diff --git a/EvoCalculator.Core.Models/Calculation/Models/FlowValues.cs b/EvoCalculator.Core.Models/Calculation/Models/FlowValues.cs new file mode 100644 index 0000000..b4597c2 --- /dev/null +++ b/EvoCalculator.Core.Models/Calculation/Models/FlowValues.cs @@ -0,0 +1,10 @@ +using System; + +namespace EvoCalculator.Core.Models.Calculation.Models +{ + public class FlowValue + { + public DateTime Date; + public double Flow; + } +} \ No newline at end of file diff --git a/EvoCalculator.Core.Models/EvoCalculator.Core.Models.csproj b/EvoCalculator.Core.Models/EvoCalculator.Core.Models.csproj index 9c38250..e4a5e90 100644 --- a/EvoCalculator.Core.Models/EvoCalculator.Core.Models.csproj +++ b/EvoCalculator.Core.Models/EvoCalculator.Core.Models.csproj @@ -5,9 +5,8 @@ - - - + + diff --git a/EvoCalculator.Core.Tests/Calculation/Suite/FinanceFormulasTests.cs b/EvoCalculator.Core.Tests/Calculation/Suite/FinanceFormulasTests.cs new file mode 100644 index 0000000..c29d2fb --- /dev/null +++ b/EvoCalculator.Core.Tests/Calculation/Suite/FinanceFormulasTests.cs @@ -0,0 +1,106 @@ +using System; +using System.Linq; +using EvoCalculator.Core.Calculation.FinanceFormulas; +using EvoCalculator.Core.Models.Calculation.Models; +using Xunit; +using Xunit.Abstractions; + +namespace EvoCalculator.Core.Tests.Calculation.Suite +{ + public class FinanceFormulasTests + { + private readonly ITestOutputHelper output; + + readonly FlowValue[] _flowValues = + { + new FlowValue + { + Date = new DateTime(2020, 09, 01), + Flow = -6500000 + }, + new FlowValue + { + Date = new DateTime(2020, 09, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2020, 10, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2020, 11, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2020, 12, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 1, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 2, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 3, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 4, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 5, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 6, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 7, 01), + Flow = 608944.445718643 + }, + new FlowValue + { + Date = new DateTime(2021, 8, 01), + Flow = 608944.445718643 + }, + }; + + public FinanceFormulasTests(ITestOutputHelper output) + { + this.output = output; + } + + [Fact] + public void XNPVTest() + { + var XNPV = new XNPV(_flowValues.Skip(1).ToArray(), 0.3); + var res = XNPV.GetResult(); + output.WriteLine(res.ToString()); + Assert.InRange(res, 6490000, 6590000); + } + + [Fact] + public void XIRRTest() + { + var XIRR = new XIRR(_flowValues); + var res = XIRR.GetResult(); + output.WriteLine(res.ToString()); + Assert.InRange(res, 0.29, 0.31); + } + } +} \ No newline at end of file diff --git a/EvoCalculator.Core.Tests/EvoCalculator.Core.Tests.csproj b/EvoCalculator.Core.Tests/EvoCalculator.Core.Tests.csproj index 5d0a1da..bc8cb62 100644 --- a/EvoCalculator.Core.Tests/EvoCalculator.Core.Tests.csproj +++ b/EvoCalculator.Core.Tests/EvoCalculator.Core.Tests.csproj @@ -13,4 +13,13 @@ + + + + + + + + + diff --git a/EvoCalculator.Core.sln.DotSettings.user b/EvoCalculator.Core.sln.DotSettings.user new file mode 100644 index 0000000..9237d20 --- /dev/null +++ b/EvoCalculator.Core.sln.DotSettings.user @@ -0,0 +1,5 @@ + + <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <Solution /> +</SessionState> + True \ No newline at end of file