├── docs ├── img │ └── logo.png ├── index.fsx ├── compatibility.fsx └── openofficediff.fsx ├── tests ├── ExcelFinancialFunctions.Tests │ ├── Program.fs │ ├── testdata │ │ ├── testdata.xlsx │ │ ├── dollarfr.test │ │ ├── dollarde.test │ │ ├── effect.test │ │ ├── nominal.test │ │ ├── rate.test │ │ ├── irr.test │ │ ├── fvschedule.test │ │ ├── sln.test │ │ ├── rri.csv │ │ ├── npv.test │ │ ├── pduration.csv │ │ ├── oddfyield.test │ │ ├── coupdays.test │ │ ├── tbilleq.test │ │ ├── xirr.test │ │ ├── syd.test │ │ ├── tbillyield.test │ │ ├── tbillprice.test │ │ ├── mirr.test │ │ ├── cumipmt.test │ │ ├── cumprinc.test │ │ └── db.test │ ├── ExcelFinancialFunctions.Tests.fsproj │ ├── concurrencytests.fs │ ├── testutils.fs │ ├── spottests.fs │ └── crosstests.fs ├── ExcelFinancialFunctions.ReleaseTests │ ├── ExcelFinancialFunctions.ReleaseTests.csproj │ ├── README.md │ └── SpotTests.cs └── ExcelFinancialFunctions.ConsoleTests │ ├── ExcelFinancialFunctions.ConsoleTests.fsproj │ ├── README.md │ ├── excel.fs │ ├── testinfrastructure.fs │ ├── testsdef.fs │ └── MatrixTests.fs ├── .github ├── dependabot.yml ├── workflows │ ├── pull-requests.yml │ ├── push-master.yml │ └── release.yml └── ISSUE_TEMPLATE.md ├── src └── ExcelFinancialFunctions │ ├── ExcelFinancialFunctions.snk │ ├── publicenums.fs │ ├── misc.fs │ ├── ExcelFinancialFunctions.fsproj │ ├── irr.fs │ ├── tbill.fs │ ├── tvm.fs │ ├── loan.fs │ ├── common.fs │ └── oddbonds.fs ├── .config └── dotnet-tools.json ├── RELEASE_NOTES.md ├── .gitattributes ├── ExcelFinancialFunctions.sln ├── .gitignore ├── PackageReadmeFile.md ├── README.md └── LICENSE.txt /docs/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/ExcelFinancialFunctions/HEAD/docs/img/logo.png -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/Program.fs: -------------------------------------------------------------------------------- 1 | module Program = 2 | 3 | [] 4 | let main _ = 0 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "nuget" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/ExcelFinancialFunctions.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/ExcelFinancialFunctions/HEAD/src/ExcelFinancialFunctions/ExcelFinancialFunctions.snk -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/testdata.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fsprojects/ExcelFinancialFunctions/HEAD/tests/ExcelFinancialFunctions.Tests/testdata/testdata.xlsx -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "fsdocs-tool": { 6 | "version": "20.0.0", 7 | "commands": [ 8 | "fsdocs" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/dollarfr.test: -------------------------------------------------------------------------------- 1 | 0.34,1,0.34 2 | 0.34,17,0.0578 3 | 0.34,20,0.068 4 | 1.02,1,1.02 5 | 1.02,17,1.0034 6 | 1.02,20,1.004 7 | 2.34,1,2.34 8 | 2.34,17,2.0578 9 | 2.34,20,2.068 10 | -1.5,1,-1.5 11 | -1.5,17,-1.085 12 | -1.5,20,-1.1 13 | 1.125,16,1.02 14 | 1.125,16,1.02 15 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/dollarde.test: -------------------------------------------------------------------------------- 1 | 0.34,1,0.34 2 | 0.34,17,2 3 | 0.34,20,1.7 4 | 1.02,1,1.02 5 | 1.02,17,1.117647058824 6 | 1.02,20,1.1 7 | 2.34,1,2.34 8 | 2.34,17,4 9 | 2.34,20,3.7 10 | -1.5,1,-1.5 11 | -1.5,17,-3.941176470588 12 | -1.5,20,-3.5 13 | 1.125,16,1.78125 14 | 1.125,16,1.78125 15 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/effect.test: -------------------------------------------------------------------------------- 1 | 0.6,1,0.6 2 | 0.6,1.7,0.6 3 | 0.6,2,0.69 4 | 0.6,10,0.7908476965429 5 | 0.6,11.3,0.793570056309 6 | 0.6,13,0.7978035299639 7 | 1.5,1,1.5 8 | 1.5,1.7,1.5 9 | 1.5,2,2.0625 10 | 1.5,10,3.045557735708 11 | 1.5,11.3,3.08028601065 12 | 1.5,13,3.135427858466 13 | 0.0525,4,0.05354266737076 14 | 0.0525,4,0.05354266737076 15 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/nominal.test: -------------------------------------------------------------------------------- 1 | 0.6,1,0.6 2 | 0.6,1.7,0.6 3 | 0.6,2,0.5298221281347 4 | 0.6,10,0.4812238946896 5 | 0.6,11.3,0.4801892443735 6 | 0.6,13,0.4786032384269 7 | 1.5,1,1.5 8 | 1.5,1.7,1.5 9 | 1.5,2,1.162277660168 10 | 1.5,10,0.9595822638522 11 | 1.5,11.3,0.9555359466673 12 | 1.5,13,0.9493548503806 13 | 0.053543,4,0.05250031986836 14 | 0.053543,4,0.05250031986836 15 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/rate.test: -------------------------------------------------------------------------------- 1 | 1,10,100,-100,EndOfPeriod,0.15,-0.1 2 | 1,10,100,-100,EndOfPeriod,0.15,-0.1 3 | 5,20,120,-50,BeginningOfPeriod,0,-0.3356185414527 4 | 5,20,120,-50,BeginningOfPeriod,0,-0.3356185414527 5 | 10,-10,0,100,EndOfPeriod,-0.15,4.334921358464E-09 6 | 10,-10,0,100,EndOfPeriod,-0.15,4.334921358464E-09 7 | 25,-40,-200,100,BeginningOfPeriod,0.15,-0.2857595837824 8 | 25,-40,-200,100,BeginningOfPeriod,0.15,-0.2857595837824 9 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/irr.test: -------------------------------------------------------------------------------- 1 | -100;10;10;100,0.15,0.06886017912484 2 | -100;10;10;100,0.5,0.06886017912484 3 | -100;10;10;100,0,0.06886017912484 4 | -100;-10;10;100,0.15,0 5 | -100;-10;10;100,0.5,-1.594280263362E-13 6 | -100;-10;10;100,0,0 7 | -200;0;10;-10;300,0.15,0.1077814124169 8 | -200;0;10;-10;300,0.5,0.1077814124169 9 | -200;0;10;-10;300,0,0.1077814124169 10 | -123;12;15;50;200,0.14,0.2609523370085 11 | -123;12;15;50;200,0.14,0.2609523370085 12 | -------------------------------------------------------------------------------- /.github/workflows/pull-requests.yml: -------------------------------------------------------------------------------- 1 | name: Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v1 14 | with: 15 | dotnet-version: 6.0.x 16 | - name: Restore dependencies 17 | run: dotnet restore 18 | - name: Build 19 | run: dotnet build --no-restore 20 | - name: Run Tests 21 | run: dotnet test --no-build --verbosity normal 22 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/fvschedule.test: -------------------------------------------------------------------------------- 1 | -300,0.3;-0.5;0.2;1.3;-0.2,-430.56 2 | -300,0.3;-0.5;0.2;0;-1.2,46.8 3 | -100,0.3;-0.5;0.2;1.3;-0.2,-143.52 4 | -100,0.3;-0.5;0.2;0;-1.2,15.6 5 | -5.4,0.3;-0.5;0.2;1.3;-0.2,-7.75008 6 | -5.4,0.3;-0.5;0.2;0;-1.2,0.8424 7 | 0,0.3;-0.5;0.2;1.3;-0.2,0 8 | 0,0.3;-0.5;0.2;0;-1.2,0 9 | 100,0.3;-0.5;0.2;1.3;-0.2,143.52 10 | 100,0.3;-0.5;0.2;0;-1.2,-15.6 11 | 150.5,0.3;-0.5;0.2;1.3;-0.2,215.9976 12 | 150.5,0.3;-0.5;0.2;0;-1.2,-23.478 13 | 100,0.13;0.14;-0.2;0.34;-0.12,121.5236352 14 | 100,0.13;0.14;-0.2;0.34;-0.12,121.5236352 15 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ReleaseTests/ExcelFinancialFunctions.ReleaseTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net461 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/sln.test: -------------------------------------------------------------------------------- 1 | 100,10,1,90 2 | 100,10,13,6.923076923077 3 | 100,10,12.7,7.086614173228 4 | 100,10,40,2.25 5 | 100,50,1,50 6 | 100,50,13,3.846153846154 7 | 100,50,12.7,3.937007874016 8 | 100,50,40,1.25 9 | 100,0,1,100 10 | 100,0,13,7.692307692308 11 | 100,0,12.7,7.874015748032 12 | 100,0,40,2.5 13 | 200,10,1,190 14 | 200,10,13,14.61538461538 15 | 200,10,12.7,14.96062992126 16 | 200,10,40,4.75 17 | 200,50,1,150 18 | 200,50,13,11.53846153846 19 | 200,50,12.7,11.81102362205 20 | 200,50,40,3.75 21 | 200,0,1,200 22 | 200,0,13,15.38461538462 23 | 200,0,12.7,15.74803149606 24 | 200,0,40,5 25 | 122,20,12,8.5 26 | 122,20,12,8.5 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | ### Description 3 | 4 | Please provide a succinct description of your issue. 5 | 6 | ### Repro steps 7 | 8 | Please provide the steps required to reproduce the problem 9 | 10 | 1. Step A 11 | 12 | 2. Step B 13 | 14 | ### Expected behavior 15 | 16 | Please provide a description of the behaviour you expect. 17 | 18 | ### Actual behavior 19 | 20 | Please provide a description of the actual behaviour you observe. 21 | 22 | ### Known workarounds 23 | 24 | Please provide a description of any known workarounds. 25 | 26 | ### Related information 27 | 28 | * Operating system 29 | * Branch 30 | * .NET Runtime, CoreCLR or Mono Version 31 | * Performance information, links to performance testing scripts 32 | 33 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/rri.csv: -------------------------------------------------------------------------------- 1 | nper,pv,fv,rri 2 | 0,300,400,#NUM! 3 | 0,-1,-3,#NUM! 4 | 1,-1,-3,2 5 | 12,100,10,-0.174595815 6 | 12,100,-90,#NUM! 7 | 5,0,0,0 8 | 5,-1,5,#NUM! 9 | 5,10,10,0 10 | 2,2,8,1 11 | 2,8,2,-0.5 12 | 2,8,0,-1 13 | 2,0,10,#NUM! 14 | 12,-5,-6,0.01530947 15 | 1,-5,0,-1 16 | 12,-1,-1,0 17 | 12,300,300,0 18 | 12,300,400,0.024263181 19 | 12,300,4000,0.240923173 20 | 12,300,40000,0.503412747 21 | 24,300,400,0.012058882 22 | 24,300,4000,0.113967312 23 | 24,300,40000,0.226137328 24 | 38,300,400,0.00759931 25 | 38,300,4000,0.070541853 26 | 38,300,40000,0.137416281 27 | 8,10000,2441880,0.988225043 28 | 4,5000,6000,0.046635139 29 | 4,5000,10000,0.189207115 30 | 1,250,275,0.1 31 | 2,250,500,0.414213562 32 | 3,250,880,0.521180984 33 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ReleaseTests/README.md: -------------------------------------------------------------------------------- 1 | # Release Tests 2 | 3 | This project tests new release candidate nuget packages after they are deployed to the NuGet Gallery. It should always include a reference to the latest released version of the package on NuGet Gallery. 4 | 5 | When a new release candidate package is uploaded, we can then update this project to refer to it, then run these tests. This helps ensure that we didn't break anything in the upgrade process. 6 | 7 | ## Differences 8 | 9 | This project has some differences from the unit tests or the interop tests. 10 | 11 | * Runs against .NET Framework 4.6.1. This is the oldest supported configuration. 12 | * Written in C#. Helps ensure that the library functions correctly in that language. 13 | * Uses the latest package from NuGet Gallery. Not the locally built code. -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/npv.test: -------------------------------------------------------------------------------- 1 | -1.5,-100;10;10;100,1760 2 | -1.5,-100;-10;10;100,1680 3 | -1.5,-200;0;10;-10;300,-9440 4 | -2,-100;10;10;100,200 5 | -2,-100;-10;10;100,180 6 | -2,-200;0;10;-10;300,-120 7 | -0.4,-100;10;10;100,679.012345679 8 | -0.4,-100;-10;10;100,623.4567901235 9 | -0.4,-200;0;10;-10;300,3493.827160494 10 | -0.1,-100;10;10;100,67.36777930194 11 | -0.1,-100;-10;10;100,42.67642127724 12 | -0.1,-200;0;10;-10;300,284.3062541279 13 | 0,-100;10;10;100,20 14 | 0,-100;-10;10;100,0 15 | 0,-200;0;10;-10;300,100 16 | 0.6,-100;10;10;100,-40.8935546875 17 | 0.6,-100;-10;10;100,-48.7060546875 18 | 0.6,-200;0;10;-10;300,-95.47424316406 19 | 1.5,-100;10;10;100,-35.2 20 | 1.5,-100;-10;10;100,-38.4 21 | 1.5,-200;0;10;-10;300,-76.544 22 | 0.14,-123;12;15;50;200,44.94119298304 23 | 0.14,-123;12;15;50;200,44.94119298304 24 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/pduration.csv: -------------------------------------------------------------------------------- 1 | rate,pv,fv,pduration 2 | 0.01530947049973120,-5,-6,#NUM! 3 | -1.00000000000000000,-5,0,#NUM! 4 | 0.00000000000000000,-1,-1,#NUM! 5 | 0.00000000000000000,300,300,#NUM! 6 | 0.10000000000000000,0,100,#NUM! 7 | 0.10000000000000000,100,0,#NUM! 8 | 0.02426318074098920,300,400,12 9 | 0.24092317318260100,300,4000,12 10 | 0.50341274654387500,300,40000,12 11 | 0.01205888205231860,300,400,24 12 | 0.11396731243901500,300,4000,24 13 | 0.22613732776711200,300,40000,24 14 | 0.00759931015463056,300,400,38 15 | 0.07054185347032280,300,4000,38 16 | 0.13741628093790000,300,40000,38 17 | 0.98822504304098700,10000,2441880,8 18 | 0.04663513939210560,5000,6000,4 19 | 0.18920711500272100,5000,10000,4 20 | 0.10000000000000000,250,275,1 21 | 0.41421356237309500,250,500,2 22 | 0.52118098430455700,250,880,3 23 | 0.02500000000000000,2000,2200,3.85986616262266 24 | 0.00208333333333333,1000,1200,87.60547641937140 25 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/ExcelFinancialFunctions.ConsoleTests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net48 4 | true 5 | 3390;$(WarnOn) 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /.github/workflows/push-master.yml: -------------------------------------------------------------------------------- 1 | name: Build+Test+Docs 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Setup .NET 13 | uses: actions/setup-dotnet@v1 14 | with: 15 | dotnet-version: 6.0.x 16 | - name: Restore dependencies 17 | run: dotnet restore 18 | - name: Build Debug 19 | run: dotnet build --no-restore 20 | - name: Run Debug Tests (Release tests fail to discover) 21 | run: dotnet test --no-build --verbosity normal 22 | - name: Build Release 23 | run: dotnet build --no-restore --configuration=Release 24 | - name: Restore tools 25 | run: dotnet tool restore 26 | - name: Run fsdocs 27 | run: dotnet fsdocs build --eval --strict --properties Configuration=Release 28 | - name: Deploy docs 29 | uses: peaceiris/actions-gh-pages@v3 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./output 33 | publish_branch: gh-pages 34 | force_orphan: true 35 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | workflow_dispatch: 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | env: 14 | FSPROJ: "src\\ExcelFinancialFunctions\\ExcelFinancialFunctions.fsproj" 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Setup .NET 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 6.0.x 21 | - name: Restore dependencies 22 | run: dotnet restore $FSPROJ 23 | - name: Build Release 24 | run: dotnet build $FSPROJ /p:Configuration=Release --no-restore --verbosity normal 25 | - name: Create NuGet package 26 | run: dotnet pack $FSPROJ /p:Configuration=Release /p:GitVersion=${GITHUB_REF#refs/tags/} /p:ReleaseNotes="${{ github.event.release.body }}" --no-build --verbosity normal 27 | - name: Publish package to NuGet Gallery (if this version not published before) 28 | run: dotnet nuget push **\*.nupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_ORG_TOKEN }} --skip-duplicate 29 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/oddfyield.test: -------------------------------------------------------------------------------- 1 | 11/11/2008 12:00:00 AM,3/1/2021 12:00:00 AM,10/15/2008 12:00:00 AM,3/1/2009 12:00:00 AM,0.0575,84.5,100,SemiAnnual,UsPsa30_360,0.0772455415973 2 | 11/11/2008 12:00:00 AM,3/1/2021 12:00:00 AM,10/15/2008 12:00:00 AM,3/1/2009 12:00:00 AM,0.0575,84.5,100,SemiAnnual,UsPsa30_360,0.0772455415973 3 | 12/11/2008 12:00:00 AM,4/1/2021 12:00:00 AM,10/15/2008 12:00:00 AM,4/1/2009 12:00:00 AM,0.06,100,100,Quarterly,ActualActual,0.05997699855589 4 | 12/11/2008 12:00:00 AM,4/1/2021 12:00:00 AM,10/15/2008 12:00:00 AM,4/1/2009 12:00:00 AM,0.06,100,100,Quarterly,ActualActual,0.05997699855589 5 | 2/28/2009 12:00:00 AM,5/30/2020 12:00:00 AM,9/15/2008 12:00:00 AM,5/30/2009 12:00:00 AM,0.05,75,89,Annual,Actual360,0.07763359756356 6 | 2/28/2009 12:00:00 AM,5/30/2020 12:00:00 AM,9/15/2008 12:00:00 AM,5/30/2009 12:00:00 AM,0.05,75,89,Annual,Actual360,0.07763359756356 7 | 10/31/2009 12:00:00 AM,12/31/2021 12:00:00 AM,10/15/2009 12:00:00 AM,12/31/2009 12:00:00 AM,0.06,100,100,Quarterly,ActualActual,0.05999989486267 8 | 10/31/2009 12:00:00 AM,12/31/2021 12:00:00 AM,10/15/2009 12:00:00 AM,12/31/2009 12:00:00 AM,0.06,100,100,Quarterly,ActualActual,0.05999989486267 9 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/ExcelFinancialFunctions.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp6.0 5 | 6 | false 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | runtime; build; native; contentfiles; analyzers; buildtransitive 24 | all 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/coupdays.test: -------------------------------------------------------------------------------- 1 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,Actual360,360 2 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,Actual365,365 3 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,ActualActual,365 4 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,Europ30_360,360 5 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,UsPsa30_360,360 6 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,SemiAnnual,Actual360,180 7 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,SemiAnnual,Actual365,182.5 8 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,SemiAnnual,ActualActual,181 9 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,SemiAnnual,Europ30_360,180 10 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,SemiAnnual,UsPsa30_360,180 11 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Quarterly,Actual360,90 12 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Quarterly,Actual365,91.25 13 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Quarterly,ActualActual,89 14 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Quarterly,Europ30_360,90 15 | 2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Quarterly,UsPsa30_360,90 16 | 3/4/1984 12:00:00 AM,4/5/1990 12:00:00 AM,Quarterly,UsPsa30_360,90 17 | 3/4/1984 12:00:00 AM,4/5/1990 12:00:00 AM,Quarterly,UsPsa30_360,90 18 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/publicenums.fs: -------------------------------------------------------------------------------- 1 | // All the enums exposed in the external API, I need to define them first becasue they are used in the internal part 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | /// Indicates when payments are due (end/beginning of period) 6 | type PaymentDue = 7 | | EndOfPeriod = 0 8 | | BeginningOfPeriod = 1 9 | 10 | /// The type of Day Count Basis 11 | type DayCountBasis = 12 | /// US 30/360 13 | | UsPsa30_360 = 0 14 | /// Actual/Actual 15 | | ActualActual = 1 16 | /// Actual/360 17 | | Actual360 = 2 18 | /// Actual/365 19 | | Actual365 = 3 20 | /// European 30/360 21 | | Europ30_360 = 4 22 | 23 | /// The number of coupon payments per year 24 | type Frequency = 25 | | Annual = 1 26 | | SemiAnnual = 2 27 | | Quarterly = 4 28 | 29 | /// Indicates whether accrued interest is computed from issue date (by default) or first interest to settlement 30 | type AccrIntCalcMethod = 31 | | FromFirstToSettlement = 0 32 | | FromIssueToSettlement = 1 33 | 34 | /// Specifies whether to switch to straight-line depreciation when depreciation is greater than the declining balance calculation 35 | type VdbSwitch = 36 | | DontSwitchToStraightLine = 1 37 | | SwitchToStraightLine = 0 38 | -------------------------------------------------------------------------------- /RELEASE_NOTES.md: -------------------------------------------------------------------------------- 1 | ## 3.2.0 - May 10 2022 2 | * Removes needless constraint on 0-value inputs to FV & PMT functions. (PR #67) 3 | 4 | ## 3.1.0 - Dec 20 2021 5 | * Adds PDURATION function. Returns the number of periods required by an investment to reach a specified value. (Resolves #62) 6 | * Adds RRI function. Also used for CAGR. Returns an equivalent interest rate for the growth of an investment. (Resolves #60) 7 | * Improves XIRR function by reducing the precision required before an answer is returned. (Fixes #27) 8 | * Improves ACCRINT function by allowing first interest date on the settlement date. (Fixes #22) 9 | * Adds PriceAllowNegativeYield function. This operates like the PRICE function except that it allows negative yield inputs. It is experimental. We'd love feedback on how this works for folks. (Fixes #13) 10 | 11 | ## 3.0.0 - Dec 7 2021 12 | * Retarget library onto .NET Standard 2.0 13 | * Adds explicit support for .NET Core 2.0 and higher including 5.0 and 6.0 14 | * Removes support for full .NET Framework 4.6 and lower 15 | 16 | ## 2.4.1 17 | * Relaxed FSharp.Core dependency 18 | 19 | ## 2.4 20 | * Only build profile 259 portable profile of the library (net40 not needed) 21 | 22 | ## 2.3 23 | * Portable version of the library 24 | 25 | ## 2.2.1 26 | * Price and yield functions now accept rate = 0 27 | 28 | ## 2.2 29 | * Rename the top-level namespace to `Excel.FinancialFunctions` 30 | 31 | ## 2.1 32 | * Move to github 33 | 34 | ## 2.0 35 | * Fixed order of parameter and naming to the Rate function 36 | 37 | ## 1.0 38 | * Fixed call to throw in bisection 39 | * Changed findBounds algo 40 | * Added TestXirrBugs function 41 | * Removed the NewValue functions everywhere 42 | 43 | 44 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/README.md: -------------------------------------------------------------------------------- 1 | # Console Tests 2 | 3 | These tests require Excel 2013 or later installed on your host machine. They directly 4 | compare the results for many operations against Excel directly using [Excel Interop](https://docs.microsoft.com/en-us/dotnet/api/microsoft.office.interop.excel). A better name for 5 | them might be "Interop Tests". 6 | 7 | They also take a very long time to run, as the test matrices get quite complex. For example on my 8 | 12-core machine, they run for 15 minutes. 9 | 10 | You can run just the "Fast" tests as a smoke test: 11 | 12 | ``` 13 | PS tests\ExcelFinancialFunctions.ConsoleTests> dotnet build 14 | 15 | Microsoft (R) Build Engine version 16.11.0+0538acc04 for .NET 16 | Copyright (C) Microsoft Corporation. All rights reserved. 17 | 18 | ExcelFinancialFunctions -> \src\ExcelFinancialFunctions\bin\Debug\netstandard2.0\ExcelFinancialFunctions.dll 19 | ExcelFinancialFunctions.ConsoleTests -> \tests\ExcelFinancialFunctions.ConsoleTests\bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll 20 | 21 | Build succeeded. 22 | 0 Warning(s) 23 | 0 Error(s) 24 | 25 | Time Elapsed 00:00:00.75 26 | 27 | PS \tests\ExcelFinancialFunctions.ConsoleTests> vstest.console.exe bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll --TestCaseFilter:"Category=Fast" 28 | 29 | Microsoft (R) Test Execution Command Line Tool Version 16.11.0 30 | Copyright (c) Microsoft Corporation. All rights reserved. 31 | 32 | A total of 1 test files matched the specified pattern. 33 | NUnit Adapter 4.1.0.0: Test execution started 34 | Running selected tests in \tests\ExcelFinancialFunctions.ConsoleTests\bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll 35 | NUnit3TestExecutor discovered 69 of 69 NUnit test cases using Current Discovery mode, Non-Explicit run 36 | Passed RunMatrix("IRR") [912 ms] 37 | ... 38 | Test Run Successful. 39 | Total tests: 69 40 | Passed: 69 41 | Total time: 5.2782 Seconds 42 | ``` -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/misc.fs: -------------------------------------------------------------------------------- 1 | // Various routings that don't have an obvious classification in other files. 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | 8 | module internal Misc = 9 | 10 | // Main formulas 11 | let dollar fractionalDollar fraction f = 12 | let aBase = floor fraction 13 | let dollar = if fractionalDollar > 0. then floor fractionalDollar else ceiling fractionalDollar 14 | let remainder = fractionalDollar - dollar 15 | let digits = pow 10. (ceiling (log10 aBase)) 16 | f aBase dollar remainder digits 17 | let dollarDe aBase dollar remainder digits = 18 | remainder * digits / aBase + dollar 19 | let dollarFr aBase dollar remainder digits = 20 | let absDigits = abs digits 21 | remainder * aBase / absDigits + dollar 22 | let effect nominalRate npery = 23 | let periods = floor npery 24 | pow (nominalRate / periods + 1.) periods - 1. 25 | let nominal effectRate npery = 26 | let periods = floor npery 27 | (pow (effectRate + 1.) (1. / periods) - 1.) * periods 28 | 29 | 30 | // Preconditions and special cases 31 | let calcDollarDe fractionalDollar fraction = 32 | (fraction > 0.) |> elseThrow "fraction must be more than 0" 33 | dollar fractionalDollar fraction dollarDe 34 | let calcDollarFr fractionalDollar fraction = 35 | (fraction > 0.) |> elseThrow "fraction must be more than 0" 36 | (pow 10. (ceiling (log10 (floor fraction))) <> 0.) |> elseThrow "10^(ceiling (log10 (floor fraction))) must be different from 0" 37 | dollar fractionalDollar fraction dollarFr 38 | let calcEffect nominalRate npery = 39 | (nominalRate > 0.) |> elseThrow "nominal rate must be more than zero" 40 | (npery >= 1.) |> elseThrow "npery must be more or equal to one" 41 | effect nominalRate npery 42 | let calcNominal effectRate npery = 43 | (effectRate > 0.) |> elseThrow "effective rate must be more than zero" 44 | (npery >= 1.) |> elseThrow "npery must be more or equal to one" 45 | nominal effectRate npery 46 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/ExcelFinancialFunctions.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | false 7 | 3390;$(WarnOn) 8 | ExcelFinancialFunctions 9 | ExcelFinancialFunctions 10 | ExcelFinancialFunctions 11 | A .NET Standard library that provides the full set of financial functions from Excel. 12 | Luca Bolognese 13 | https://github.com/fsprojects/ExcelFinancialFunctions 14 | git 15 | ExcelFinancialFunctions 16 | $(GitVersion) 17 | $(ReleaseNotes) 18 | PackageReadmeFile.md 19 | Apache-2.0 20 | https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/LICENSE.txt 21 | logo.png 22 | https://fsprojects.github.io/ExcelFinancialFunctions 23 | excel;finance;fsharp;csharp 24 | True 25 | ExcelFinancialFunctions.snk 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/concurrencytests.fs: -------------------------------------------------------------------------------- 1 | namespace Excel.FinancialFunctions.Tests 2 | 3 | open NUnit.Framework 4 | 5 | // These tests check that nothing goes wrong with the memoization which is used in the internal dayCount function 6 | // that is called by the functions tested below - when used concurrently. 7 | 8 | [] 9 | module ConcurrencyTests = 10 | open System 11 | open Excel.FinancialFunctions 12 | open TestPreconditions 13 | 14 | let parallelCount = 8 15 | 16 | let TestParallel (f : unit -> unit) = 17 | let expected = true 18 | let actual = 19 | try 20 | [|1..parallelCount|] 21 | |> Array.Parallel.iter (fun _ -> f()) 22 | true 23 | with 24 | | _ -> false 25 | 26 | Assert.AreEqual(expected, actual) 27 | 28 | let startDate = DateTime(2000, 1, 1) 29 | let endDate = DateTime(2010, 1, 1) 30 | 31 | [] 32 | let YearFracWorksConcurrently() = 33 | let f = fun () -> (Financial.YearFrac(startDate, endDate, DayCountBasis.Actual365) |> ignore) 34 | TestParallel f 35 | 36 | [] 37 | let CoupDaysWorksConcurrently() = 38 | let f = fun () -> (Financial.CoupDays(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 39 | TestParallel f 40 | 41 | [] 42 | let CoupPCDWorksConcurrently() = 43 | let f = fun () -> (Financial.CoupPCD(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 44 | TestParallel f 45 | 46 | [] 47 | let CoupNCDWorksConcurrently() = 48 | let f = fun () -> (Financial.CoupNCD(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 49 | TestParallel f 50 | 51 | [] 52 | let CoupNumWorksConcurrently() = 53 | let f = fun () -> (Financial.CoupNum(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 54 | TestParallel f 55 | 56 | [] 57 | let CoupDaysBSWorksConcurrently() = 58 | let f = fun () -> (Financial.CoupDaysBS(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 59 | TestParallel f 60 | 61 | [] 62 | let CoupDaysNCWorksConcurrently() = 63 | let f = fun () -> (Financial.CoupDaysNC(startDate, endDate, Frequency.Quarterly, DayCountBasis.Actual365) |> ignore) 64 | TestParallel f 65 | 66 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testutils.fs: -------------------------------------------------------------------------------- 1 | #nowarn "25" 2 | 3 | namespace Excel.FinancialFunctions.Tests 4 | 5 | open FsCheck 6 | open NUnit.Framework 7 | open System 8 | open System.IO 9 | 10 | [] 11 | module internal TestUtils = 12 | [] 13 | let PRECISION = 1e-6 14 | 15 | let readTestData fname = 16 | Path.Combine(__SOURCE_DIRECTORY__, "testdata", fname + ".test") 17 | |> File.ReadAllLines 18 | |> Seq.filter (fun line -> not (String.IsNullOrEmpty line)) 19 | |> Seq.map (fun line -> line.Split [| ',' |]) 20 | 21 | let inline shouldEqual msg exp act = 22 | Assert.AreEqual(exp, float act, PRECISION, msg) 23 | 24 | let inline runTests fname parsef f = 25 | readTestData fname 26 | |> Seq.iteri (fun i data -> 27 | let param, expected = parsef data 28 | let actual = f param 29 | shouldEqual (sprintf "%d - %s(%A)" i fname param) expected actual) 30 | 31 | let inline parse str = 32 | let mutable res = Unchecked.defaultof<_> 33 | let _ = (^a: (static member TryParse: string * byref< ^a > -> bool) (str, &res)) 34 | res 35 | 36 | let inline parseArray (str: string) = str.Split [| ';' |] |> Array.map Double.Parse 37 | 38 | let inline parseDateArray (str: string) = str.Split [| ';' |] |> Array.map DateTime.Parse 39 | 40 | // universal parse methods for function arguments and result 41 | let inline parse3 [| a; b; c |] = 42 | (parse a, parse b), parse c 43 | let inline parse4 [| a; b; c; d |] = 44 | (parse a, parse b, parse c), parse d 45 | let inline parse5 [| a; b; c; d; e |] = 46 | (parse a, parse b, parse c, parse d), parse e 47 | let inline parse6 [| a; b; c; d; e; f |] = 48 | (parse a, parse b, parse c, parse d, parse e), parse f 49 | let inline parse7 [| a; b; c; d; e; f; g |] = 50 | (parse a, parse b, parse c, parse d, parse e, parse f), parse g 51 | let inline parse8 [| a; b; c; d; e; f; g; h |] = 52 | (parse a, parse b, parse c, parse d, parse e, parse f, parse g), parse h 53 | let inline parse9 [| a; b; c; d; e; f; g; h; i |] = 54 | (parse a, parse b, parse c, parse d, parse e, parse f, parse g, parse h), parse i 55 | let inline parse10 [| a; b; c; d; e; f; g; h; i; j |] = 56 | (parse a, parse b, parse c, parse d, parse e, parse f, parse g, parse h, parse i), parse j 57 | 58 | 59 | let private nUnitRunner = 60 | { new IRunner with 61 | member x.OnStartFixture t = () 62 | member x.OnArguments(ntest, args, every) = () 63 | member x.OnShrink(args, everyShrink) = () 64 | member x.OnFinished(name, result) = 65 | match result with 66 | | TestResult.True (data, _) -> 67 | printfn "%s" (Runner.onFinishedToString name result) 68 | | _ -> Assert.Fail(Runner.onFinishedToString name result) } 69 | 70 | let private nUnitConfig = { Config.Default with Runner = nUnitRunner } 71 | 72 | let fsCheck testable = 73 | FsCheck.Check.One (nUnitConfig, testable) 74 | 75 | let inline toFloat x = float x / float Int32.MaxValue -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ReleaseTests/SpotTests.cs: -------------------------------------------------------------------------------- 1 | using NUnit.Framework; 2 | using Excel.FinancialFunctions; 3 | using System; 4 | 5 | namespace ExcelFinancialFunctions.ReleaseTests 6 | { 7 | [DefaultFloatingPointTolerance(1e-6)] 8 | public class SpotTests 9 | { 10 | [Test(ExpectedResult = -796.374758)] 11 | public double Readme1() 12 | => Financial.IPmt(rate: 0.005, per: 53, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod); 13 | 14 | [Test(ExpectedResult = -1687.713656)] 15 | public double Readme2() 16 | => Financial.Pmt(rate: 0.005, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod); 17 | 18 | [Test(ExpectedResult = -0.67428578540657702)] 19 | public double YieldIssue8() 20 | => Financial.Yield(new DateTime(2015, 9, 21), new DateTime(2015, 10, 15), 0.04625, 105.124, 100.0, Frequency.SemiAnnual, DayCountBasis.UsPsa30_360); 21 | 22 | [Test(ExpectedResult = 0.065)] 23 | public double spotYield() 24 | => Financial.Yield(new DateTime(2008, 2, 15), new DateTime(2016, 11, 15), 0.0575, 95.04287, 100.0, Frequency.SemiAnnual, DayCountBasis.UsPsa30_360); 25 | 26 | [Test(ExpectedResult = 1.375214)] 27 | public double spotXnpv() 28 | => Financial.XNpv(0.14, new double[] { 1.0, 3.0, 4.0 }, new DateTime[] { new DateTime(1970, 3, 2), new DateTime(1988, 2, 3), new DateTime(1999, 3, 5) }); 29 | 30 | [Test(ExpectedResult = 90.0)] 31 | public double CoupDays() 32 | => Financial.CoupDays(new DateTime(1984, 3, 4), new DateTime(1990, 4, 5), Frequency.Quarterly, DayCountBasis.UsPsa30_360); 33 | 34 | [Test(ExpectedResult = 59.0)] 35 | public double CoupDaysBS() 36 | => Financial.CoupDaysBS(new DateTime(1984,3,4), new DateTime(1990,4,5),Frequency.Quarterly,DayCountBasis.UsPsa30_360 ); 37 | 38 | [Test(ExpectedResult = 31.0)] 39 | public double CoupDaysNC() 40 | => Financial.CoupDaysNC(new DateTime(1984, 3, 4), new DateTime(1990, 4, 5), Frequency.Quarterly, DayCountBasis.UsPsa30_360); 41 | 42 | [Test(ExpectedResult = 25.0)] 43 | public double CoupNum() 44 | => Financial.CoupNum(new DateTime(1984, 3, 4), new DateTime(1990, 4, 5), Frequency.Quarterly, DayCountBasis.UsPsa30_360); 45 | 46 | [Test(ExpectedResult = 1.78125)] 47 | public double DollarDe() 48 | => Financial.DollarDe(1.125,16.0); 49 | 50 | [Test(ExpectedResult = 1.02)] 51 | public double DollarFr() 52 | => Financial.DollarFr(1.125, 16.0); 53 | 54 | [Test(ExpectedResult = 0.05354266737)] 55 | public double Effect() 56 | => Financial.Effect(0.0525, 4.0); 57 | 58 | [Test(ExpectedResult = 121.5236352)] 59 | public double FvSchedule() 60 | => Financial.FvSchedule(100.0, new double[] { 0.13, 0.14, -0.2, 0.34, -0.12 }); 61 | 62 | [Test(ExpectedResult = 0.260952337)] 63 | public double Irr() 64 | => Financial.Irr(new double[] { -123.0, 12.0, 15.0, 50.0, 200.0 }, 0.14); 65 | 66 | [Test(ExpectedResult = -10.5)] 67 | public double ISPmt() 68 | => Financial.ISPmt(0.15, 3.0, 10.0, 100.0); 69 | 70 | [Test(ExpectedResult = 0.2409336873)] 71 | public double Mirr() 72 | => Financial.Mirr(new double[] { -123.0, 12.0, 15.0, 50.0, 200.0 }, 0.14, 0.12); 73 | 74 | } 75 | } -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/tbilleq.test: -------------------------------------------------------------------------------- 1 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.01,0.01014706291179 2 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.01,0.01015157836184 3 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.01,0.01015977286645 4 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.25,0.2672035139092 5 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.01,0.01019063395621 6 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.25,0.2903362798279 7 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.75,1.22881817441 8 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.01,0.0101922877413 9 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.25,0.2918378544924 10 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.75,1.265095264028 11 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.01,0.01014339706536 12 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.01,0.01015157836184 13 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.01,0.01018443594966 14 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.25,0.285379202502 15 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.75,1.144200626959 16 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.01,0.01019063395621 17 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.25,0.2903362798279 18 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.75,1.22881817441 19 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.01,0.01015157836184 20 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.01,0.01019063395621 21 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.25,0.2903362798279 22 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.75,1.22881817441 23 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,0.01,0.01015157836184 24 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.01,0.01019063395621 25 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.25,0.2903362798279 26 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.75,1.22881817441 27 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,0.01,0.01015157836184 28 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.01,0.01019063395621 29 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.25,0.2903362798279 30 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.75,1.22881817441 31 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.01,0.01020479352113 32 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.25,0.3033053166187 33 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.75,1.588499572437 34 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.01,0.01021367679499 35 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.25,0.311830002241 36 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.75,1.932789175562 37 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,0.01,0.01015157836184 38 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.01,0.01019063395621 39 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.25,0.2903362798279 40 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.75,1.22881817441 41 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,0.01,0.01015157836184 42 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.01,0.01019063395621 43 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.25,0.2903362798279 44 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.75,1.22881817441 45 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,0.01,0.01015157836184 46 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.01,0.01019063395621 47 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.25,0.2903362798279 48 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.75,1.22881817441 49 | 2/13/2008 12:00:00 AM,1/11/2009 12:00:00 AM,0.25,0.3082483889053 50 | 2/13/2008 12:00:00 AM,1/11/2009 12:00:00 AM,0.25,0.3082483889053 51 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/irr.fs: -------------------------------------------------------------------------------- 1 | // Finding internal rate of return routines. I use a different algo then excel. The results might be different. 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | open Excel.FinancialFunctions.Tvm 8 | 9 | module internal Irr = 10 | 11 | // Main formulas 12 | let npv r cfs = cfs |> Seq.mapi (fun i cf -> cf * pvFactor r (float (i+1))) |> Seq.sumBy idem 13 | let irr cfs guess = findRoot (fun r -> npv r cfs) guess 14 | let mirr cfs financeRate reinvestRate = 15 | let n = float (Seq.length cfs) 16 | let positives = cfs |> Seq.map (fun cf -> if cf > 0. then cf else 0.) 17 | let negatives = cfs |> Seq.map (fun cf -> if cf < 0. then cf else 0.) 18 | (((- npv reinvestRate positives) * ((1. + reinvestRate) ** n))/ 19 | (( npv financeRate negatives) * ( 1. + financeRate ))) ** (1./(n - 1.)) - 1. 20 | let xnpv r cfs dates = 21 | let d0 = Seq.head dates 22 | cfs |> Seq.map2 (fun d cf -> cf / ((1. + r) ** (float (days d d0) / 365.))) dates |> Seq.sumBy idem 23 | let xirr cfs dates guess = findRoot (fun r -> xnpv r cfs dates) guess 24 | 25 | // Preconditions and special cases 26 | let validCfs cfs = 27 | let rec _validCfs cfs pos neg = 28 | if pos && neg then true 29 | else match cfs with 30 | | h::t when h > 0. -> _validCfs t true neg 31 | | h::t when h <= 0. -> _validCfs t pos true 32 | | [] -> false 33 | | _ -> failwith "Should never get here" 34 | _validCfs (Seq.toList cfs) false false 35 | let calcIrr cfs guess = 36 | validCfs cfs |> elseThrow "There must be one positive and one negative cash flow" 37 | irr cfs guess 38 | let calcNpv r cfs = 39 | ( r <> -1.) |> elseThrow "r cannot be -100%" 40 | npv r cfs 41 | let calcMirr cfs financeRate reinvestRate = 42 | ( financeRate <> -1.) |> elseThrow "financeRate cannot be -100%" 43 | ( reinvestRate <> -1.) |> elseThrow "reinvestRate cannot be -100%" 44 | ( Seq.length cfs <> 1) |> elseThrow "cfs must contain more than one cashflow" 45 | ( (npv financeRate (cfs |> Seq.map (fun cf -> if cf < 0. then cf else 0.))) <> 0. ) |> elseThrow "The NPV calculated using financeRate and the negative cashflows in cfs must be different from zero" 46 | mirr cfs financeRate reinvestRate 47 | let calcXnpv r cfs dates = 48 | ( r <> -1.) |> elseThrow "r cannot be -100%" 49 | not(Seq.exists (fun x -> x < Seq.head dates) dates) |> elseThrow "In dates, one date is less than the first date" 50 | (Seq.length cfs = Seq.length dates) |> elseThrow "cfs and dates must have the same length" 51 | xnpv r cfs dates 52 | let calcXirr cfs dates guess = 53 | validCfs cfs |> elseThrow "There must be one positive and one negative cash flow" 54 | not(Seq.exists (fun x -> x < Seq.head dates) dates) |> elseThrow "In dates, one date is less than the first date" 55 | (Seq.length cfs = Seq.length dates) |> elseThrow "cfs and dates must have the same length" 56 | xirr cfs dates guess 57 | 58 | 59 | -------------------------------------------------------------------------------- /docs/index.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | // This block of code is omitted in the generated HTML documentation. Use 3 | // it to define helpers that you do not want to show in the documentation. 4 | #I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0" 5 | 6 | (** 7 | 8 | Excel Financial Functions 9 | =================== 10 | 11 | This is a .NET library that provides the full set of financial functions from Excel. 12 | It can be used from both F# and C# as well as from other .NET languages. 13 | The main goal for the library is compatibility with Excel, by providing the same functions, 14 | with the same behaviour. 15 | 16 | Note though that this is not a wrapper over the Excel library; the functions have been 17 | re-implemented in managed code so that you do not need to have Excel installed to use this library. 18 | 19 | The package is available on NuGet. [![NuGet Status](//img.shields.io/nuget/v/ExcelFinancialFunctions?style=flat)](https://www.nuget.org/packages/ExcelFinancialFunctions/) 20 | 21 | You can also use `ExcelFinancialFunctions` in [dotnet interactive](https://github.com/dotnet/interactive) 22 | notebooks, in [Visual Studio Code](https://code.visualstudio.com/) 23 | or [Jupyter](https://jupyter.org/), or in F# scripts (`.fsx` files), 24 | by referencing the package as follows: 25 | 26 | #r "nuget: ExcelFinancialFunctions" // Use the latest version 27 | 28 | Example 29 | ------- 30 | 31 | This example demonstrates using the YIELD function to calculate bond yield. 32 | 33 | *) 34 | #r "ExcelFinancialFunctions.dll" 35 | open System 36 | open Excel.FinancialFunctions 37 | 38 | // returns 0.065 or 6.5% 39 | Financial.Yield (DateTime(2008,2,15), DateTime(2016,11,15), 0.0575, 95.04287, 100.0, 40 | Frequency.SemiAnnual, DayCountBasis.UsPsa30_360) 41 | 42 | 43 | (** 44 | 45 | Samples & documentation 46 | ----------------------- 47 | 48 | The library comes with comprehensible documentation. The tutorials and articles are 49 | automatically generated from `*.fsx` files in [the docs folder][docs]. The API 50 | reference is automatically generated from Markdown comments in the library implementation. 51 | 52 | * [API Reference](reference/index.html) contains automatically generated documentation for all types, modules 53 | and functions in the library. This includes the links to the Excel documentation. 54 | * [Excel Compatibility](compatibility.html) section explains the possible differences with Excel's results. 55 | 56 | Contributing and copyright 57 | -------------------------- 58 | 59 | The project is hosted on [GitHub][gh] where you can [report issues][issues], fork 60 | the project and submit pull requests. If you're adding new public API, please also 61 | consider adding [samples][content] that can be turned into a documentation. 62 | 63 | The library was originally developed by Luca Bolognese, the initial version can be 64 | downloaded [here][msdn]. It is available under Apache License, for more information 65 | see the [License file][license] in the GitHub repository. 66 | 67 | [content]: https://github.com/fsprojects/ExcelFinancialFunctions/tree/master/docs/content 68 | [gh]: https://github.com/fsprojects/ExcelFinancialFunctions 69 | [issues]: https://github.com/fsprojects/ExcelFinancialFunctions/issues 70 | [readme]: https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/README.md 71 | [license]: https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/LICENSE.txt 72 | [msdn]: http://code.msdn.microsoft.com/office/Excel-Financial-functions-6afc7d42 73 | *) 74 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/tbill.fs: -------------------------------------------------------------------------------- 1 | // Very simple TBill mathematics. The only interesting thing is the 'if' statement on line 17. 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | open Excel.FinancialFunctions.DayCount 8 | 9 | module internal TBill = 10 | 11 | // Main formulas 12 | let getDsm settlement maturity basis = 13 | let dc = dayCount basis 14 | dc.DaysBetween settlement maturity NumDenumPosition.Numerator 15 | let TBillEq settlement maturity discount = 16 | let dsm = getDsm settlement maturity DayCountBasis.Actual360 17 | if dsm > 182. then 18 | let price = (100. - discount * 100. * dsm / 360.) / 100. 19 | let days = if dsm = 366. then 366. else 365. 20 | let tempTerm2 = (pow (dsm / days) 2.) - (2. * dsm / days - 1.) * (1. - 1. / price) 21 | let term2 = sqr tempTerm2 22 | let term3 = 2. * dsm / days - 1. 23 | 2. * (term2 - dsm / days) / term3 24 | else 25 | // This is the algo in the docs, but it is valid just above 182 ... 26 | 365. * discount / (360. - discount * dsm) 27 | let TBillYield settlement maturity pr = 28 | let dsm = getDsm settlement maturity DayCountBasis.ActualActual 29 | (100. - pr) / pr * 360. / dsm 30 | let TBillPrice settlement maturity discount = 31 | let dsm = getDsm settlement maturity DayCountBasis.ActualActual 32 | 100. * (1. - discount * dsm / 360.) 33 | 34 | // Preconditions and special cases 35 | let calcTBillEq settlement maturity discount = 36 | let dsm = getDsm settlement maturity DayCountBasis.Actual360 37 | let price = (100. - discount * 100. * dsm / 360.) / 100. 38 | let days = if dsm = 366. then 366. else 365. 39 | let tempTerm2 = (pow (dsm / days) 2.) - (2. * dsm / days - 1.) * (1. - 1. / price) 40 | (tempTerm2 >= 0.) |> elseThrow "(dsm / days)^2 - (2. * dsm / days - 1.) * (1. - 1. / (100. - discount * 100. * dsm / 360.) / 100.) must be positive" 41 | (2. * dsm / days - 1. <> 0.) |> elseThrow "2. * dsm / days - 1. must be different from 0" 42 | (maturity > settlement) |> elseThrow "maturity must be after settlement" 43 | (maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement" 44 | (discount > 0.) |> elseThrow "investment must be more than 0" 45 | TBillEq settlement maturity discount 46 | let calcTBillYield settlement maturity pr = 47 | (maturity > settlement) |> elseThrow "maturity must be after settlement" 48 | (maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement" 49 | (pr > 0.) |> elseThrow "pr must be more than 0" 50 | TBillYield settlement maturity pr 51 | let calcTBillPrice settlement maturity discount = 52 | let dsm = getDsm settlement maturity DayCountBasis.ActualActual 53 | (100. * (1. - discount * dsm / 360.)) > 0. |> elseThrow "a result less than zero triggers an exception" 54 | (maturity > settlement) |> elseThrow "maturity must be after settlement" 55 | (maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement" 56 | (discount > 0.) |> elseThrow "discount must be more than 0" 57 | TBillPrice settlement maturity discount 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /ExcelFinancialFunctions.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31702.278 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ExcelFinancialFunctions", "src\ExcelFinancialFunctions\ExcelFinancialFunctions.fsproj", "{8853070C-D0B3-4DE6-B80B-B1D10F839359}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{7F0B1813-150B-4B30-8872-625C37BD67D4}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{5E196AFF-81D3-4BD5-964A-5A9E642CD104}" 11 | ProjectSection(SolutionItems) = preProject 12 | docs\content\compatibility.fsx = docs\content\compatibility.fsx 13 | docs\content\index.fsx = docs\content\index.fsx 14 | docs\content\openofficediff.fsx = docs\content\openofficediff.fsx 15 | EndProjectSection 16 | EndProject 17 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{6FEE0C58-7CBD-4091-8D56-55AA733E1131}" 18 | ProjectSection(SolutionItems) = preProject 19 | docs\tools\generate.fsx = docs\tools\generate.fsx 20 | docs\tools\templates\template.cshtml = docs\tools\templates\template.cshtml 21 | EndProjectSection 22 | EndProject 23 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{6A5078DA-C727-406C-B792-ECC502BAA3D9}" 24 | ProjectSection(SolutionItems) = preProject 25 | build.fsx = build.fsx 26 | README.md = README.md 27 | RELEASE_NOTES.md = RELEASE_NOTES.md 28 | EndProjectSection 29 | EndProject 30 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{499DEF1A-0386-4DD9-B276-3CCC6002639E}" 31 | EndProject 32 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A38472EB-1686-402D-8619-EC2BA407C111}" 33 | EndProject 34 | Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ExcelFinancialFunctions.Tests", "tests\ExcelFinancialFunctions.Tests\ExcelFinancialFunctions.Tests.fsproj", "{17FE313F-5304-448C-892E-451E17EEEE83}" 35 | EndProject 36 | Global 37 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 38 | Debug|Any CPU = Debug|Any CPU 39 | Release|Any CPU = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 42 | {8853070C-D0B3-4DE6-B80B-B1D10F839359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 43 | {8853070C-D0B3-4DE6-B80B-B1D10F839359}.Debug|Any CPU.Build.0 = Debug|Any CPU 44 | {8853070C-D0B3-4DE6-B80B-B1D10F839359}.Release|Any CPU.ActiveCfg = Release|Any CPU 45 | {8853070C-D0B3-4DE6-B80B-B1D10F839359}.Release|Any CPU.Build.0 = Release|Any CPU 46 | {17FE313F-5304-448C-892E-451E17EEEE83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {17FE313F-5304-448C-892E-451E17EEEE83}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {17FE313F-5304-448C-892E-451E17EEEE83}.Release|Any CPU.ActiveCfg = Release|Any CPU 49 | {17FE313F-5304-448C-892E-451E17EEEE83}.Release|Any CPU.Build.0 = Release|Any CPU 50 | EndGlobalSection 51 | GlobalSection(SolutionProperties) = preSolution 52 | HideSolutionNode = FALSE 53 | EndGlobalSection 54 | GlobalSection(NestedProjects) = preSolution 55 | {8853070C-D0B3-4DE6-B80B-B1D10F839359} = {499DEF1A-0386-4DD9-B276-3CCC6002639E} 56 | {5E196AFF-81D3-4BD5-964A-5A9E642CD104} = {7F0B1813-150B-4B30-8872-625C37BD67D4} 57 | {6FEE0C58-7CBD-4091-8D56-55AA733E1131} = {7F0B1813-150B-4B30-8872-625C37BD67D4} 58 | {17FE313F-5304-448C-892E-451E17EEEE83} = {A38472EB-1686-402D-8619-EC2BA407C111} 59 | EndGlobalSection 60 | GlobalSection(ExtensibilityGlobals) = postSolution 61 | SolutionGuid = {F7A0269D-790B-4C1C-807C-7415B62CD59B} 62 | EndGlobalSection 63 | EndGlobal 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Xamarin Studio / monodevelop user-specific 10 | *.userprefs 11 | *.dll.mdb 12 | *.exe.mdb 13 | 14 | # Build results 15 | 16 | [Dd]ebug/ 17 | [Rr]elease/ 18 | x64/ 19 | build/ 20 | [Bb]in/ 21 | [Oo]bj/ 22 | 23 | # MSTest test Results 24 | [Tt]est[Rr]esult*/ 25 | [Bb]uild[Ll]og.* 26 | 27 | *_i.c 28 | *_p.c 29 | *.ilk 30 | *.meta 31 | *.obj 32 | *.pch 33 | *.pdb 34 | *.pgc 35 | *.pgd 36 | *.rsp 37 | *.sbr 38 | *.tlb 39 | *.tli 40 | *.tlh 41 | *.tmp 42 | *.tmp_proj 43 | *.log 44 | *.vspscc 45 | *.vssscc 46 | .builds 47 | *.pidb 48 | *.log 49 | *.scc 50 | 51 | # Visual C++ cache files 52 | ipch/ 53 | *.aps 54 | *.ncb 55 | *.opensdf 56 | *.sdf 57 | *.cachefile 58 | 59 | # Visual Studio profiler 60 | *.psess 61 | *.vsp 62 | *.vspx 63 | 64 | # Other Visual Studio data 65 | .vs/ 66 | 67 | # Guidance Automation Toolkit 68 | *.gpState 69 | 70 | # ReSharper is a .NET coding add-in 71 | _ReSharper*/ 72 | *.[Rr]e[Ss]harper 73 | 74 | # TeamCity is a build add-in 75 | _TeamCity* 76 | 77 | # DotCover is a Code Coverage Tool 78 | *.dotCover 79 | 80 | # NCrunch 81 | *.ncrunch* 82 | .*crunch*.local.xml 83 | 84 | # Installshield output folder 85 | [Ee]xpress/ 86 | 87 | # DocProject is a documentation generator add-in 88 | DocProject/buildhelp/ 89 | DocProject/Help/*.HxT 90 | DocProject/Help/*.HxC 91 | DocProject/Help/*.hhc 92 | DocProject/Help/*.hhk 93 | DocProject/Help/*.hhp 94 | DocProject/Help/Html2 95 | DocProject/Help/html 96 | 97 | # Click-Once directory 98 | publish/ 99 | 100 | # Publish Web Output 101 | *.Publish.xml 102 | 103 | # Enable nuget.exe in the .nuget folder (though normally executables are not tracked) 104 | !.nuget/NuGet.exe 105 | 106 | # Windows Azure Build Output 107 | csx 108 | *.build.csdef 109 | 110 | # Windows Store app package directory 111 | AppPackages/ 112 | 113 | # Others 114 | sql/ 115 | *.Cache 116 | ClientBin/ 117 | [Ss]tyle[Cc]op.* 118 | ~$* 119 | *~ 120 | *.dbmdl 121 | *.[Pp]ublish.xml 122 | *.pfx 123 | *.publishsettings 124 | 125 | # RIA/Silverlight projects 126 | Generated_Code/ 127 | 128 | # Backup & report files from converting an old project file to a newer 129 | # Visual Studio version. Backup files are not needed, because we have git ;-) 130 | _UpgradeReport_Files/ 131 | Backup*/ 132 | UpgradeLog*.XML 133 | UpgradeLog*.htm 134 | 135 | # SQL Server files 136 | App_Data/*.mdf 137 | App_Data/*.ldf 138 | 139 | 140 | #LightSwitch generated files 141 | GeneratedArtifacts/ 142 | _Pvt_Extensions/ 143 | ModelManifest.xml 144 | 145 | # ========================= 146 | # Windows detritus 147 | # ========================= 148 | 149 | # Windows image file caches 150 | Thumbs.db 151 | ehthumbs.db 152 | 153 | # Folder config file 154 | Desktop.ini 155 | 156 | # Recycle Bin used on file shares 157 | $RECYCLE.BIN/ 158 | 159 | # Mac desktop service store files 160 | .DS_Store 161 | 162 | # =================================================== 163 | # Exclude F# project specific directories and files 164 | # =================================================== 165 | 166 | # NuGet Packages Directory 167 | packages/ 168 | 169 | # Generated documentation folder 170 | docs/output/ 171 | 172 | # Temp folder used for publishing docs 173 | temp/ 174 | 175 | # Test results produced by build 176 | TestResults.xml 177 | 178 | # Nuget outputs 179 | nuget/*.nupkg 180 | release.cmd 181 | release.sh 182 | localpackages/ 183 | paket-files 184 | *.orig 185 | .paket/paket.exe 186 | docs/content/license.md 187 | docs/content/release-notes.md 188 | .fake 189 | docs/tools/FSharp.Formatting.svclog 190 | tmp 191 | .ionide 192 | .fsdocs 193 | output -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/xirr.test: -------------------------------------------------------------------------------- 1 | -100;10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0.15,0.01563509255648 2 | -100;10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0.5,0.015635099262 3 | -100;10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0,0.01563509277344 4 | -100;10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0.15,0.01183557361364 5 | -100;10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0.5,0.01183557882905 6 | -100;10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0,0.01183557128906 7 | -100;10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0.15,0.01052398234606 8 | -100;10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0.5,0.01052397862077 9 | -100;10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0,0.01052397949219 10 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0.15,4.470348358154E-09 11 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0.5,3.725290298462E-09 12 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/12/1972 12:00:00 AM;4/23/1980 12:00:00 AM;3/30/1983 12:00:00 AM,0,4.8828125E-09 13 | -100;-10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0.15,4.470348358154E-09 14 | -100;-10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0.5,3.725290298462E-09 15 | -100;-10;10;100,4/1/1970 12:00:00 AM;4/12/1973 12:00:00 AM;5/23/1983 12:00:00 AM;4/30/1987 12:00:00 AM,0,4.8828125E-09 16 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0.15,4.470348358154E-09 17 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0.5,3.725290298462E-09 18 | -100;-10;10;100,4/1/1970 12:00:00 AM;2/14/1974 12:00:00 AM;6/26/1985 12:00:00 AM;5/4/1989 12:00:00 AM,0,4.8828125E-09 19 | -1;3;4,3/2/1970 12:00:00 AM;2/3/1988 12:00:00 AM;3/5/1999 12:00:00 AM,0.14,0.08833194077015 20 | -1;3;4,3/2/1970 12:00:00 AM;2/3/1988 12:00:00 AM;3/5/1999 12:00:00 AM,0.14,0.08833194077015 21 | -185550.98;-231887.53;-26756.74;-384010.86;-27114.54;-458667.97;-217853.67;-424924.25;-75076.01;-389630.32;-112094.2;-25646.4;-24164.69;-1222.08;-556.91;1204954.004,5/15/2008 12:00:00 AM;5/19/2008 12:00:00 AM;5/30/2008 12:00:00 AM;6/20/2008 12:00:00 AM;6/26/2008 12:00:00 AM;8/21/2008 12:00:00 AM;9/8/2008 12:00:00 AM;10/13/2008 12:00:00 AM;10/14/2008 12:00:00 AM;10/24/2008 12:00:00 AM;11/19/2008 12:00:00 AM;11/21/2008 12:00:00 AM;11/21/2008 12:00:00 AM;11/21/2008 12:00:00 AM;12/3/2008 12:00:00 AM;12/5/2008 12:00:00 AM,-0.1,-0.9549350477522 22 | -185550.98;-231887.53;-26756.74;-384010.86;-27114.54;-458667.97;-217853.67;-424924.25;-75076.01;-389630.32;-112094.2;-25646.4;-24164.69;-1222.08;-556.91;1204954.004,5/15/2008 12:00:00 AM;5/19/2008 12:00:00 AM;5/30/2008 12:00:00 AM;6/20/2008 12:00:00 AM;6/26/2008 12:00:00 AM;8/21/2008 12:00:00 AM;9/8/2008 12:00:00 AM;10/13/2008 12:00:00 AM;10/14/2008 12:00:00 AM;10/24/2008 12:00:00 AM;11/19/2008 12:00:00 AM;11/21/2008 12:00:00 AM;11/21/2008 12:00:00 AM;11/21/2008 12:00:00 AM;12/3/2008 12:00:00 AM;12/5/2008 12:00:00 AM,-0.1,-0.9549350477522 23 | 105091006;-103250941.8647,4/10/2000 12:00:00 AM;4/30/2000 12:00:00 AM,-0.1,-0.2755730099976 24 | 105091006;-103250941.8647,4/10/2000 12:00:00 AM;4/30/2000 12:00:00 AM,-0.1,-0.2755730099976 25 | 206101714.8494;-156650972.5427,2/28/2001 12:00:00 AM;3/31/2001 12:00:00 AM,-0.1,-0.9604521892965 26 | 206101714.8494;-156650972.5427,2/28/2001 12:00:00 AM;3/31/2001 12:00:00 AM,-0.1,-0.9604521892965 27 | -------------------------------------------------------------------------------- /PackageReadmeFile.md: -------------------------------------------------------------------------------- 1 | # Excel Financial Functions 2 | 3 | This is a .NET Standard library that provides the full set of financial functions from Excel. The main goal for the library is compatibility with Excel, by providing the same functions, with the same behaviour. Note though that this is not a wrapper over the Excel library; the functions have been re-implemented in managed code so that you do not need to have Excel installed to use this library. 4 | 5 | [![NuGet Badge](https://img.shields.io/nuget/v/ExcelFinancialFunctions.svg?style=flat)](https://www.nuget.org/packages/ExcelFinancialFunctions/) 6 | [![Build+Test+Docs](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml/badge.svg)](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml) 7 | 8 | ## Goal: Match Excel 9 | 10 | We replicate the results Excel would produce in every situation, 11 | even in cases where we might disagree with Excel\'s approach. Please have a look at the [Compatibility](http://fsprojects.github.io/ExcelFinancialFunctions/compatibility.html) page for more detail on this topic. 12 | 13 | Microsoft\'s official documentation on [Excel Functions](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb) is the best place to learn more about how the functions should work. The scope for this library is the full set of functions in the "Financial Functions" category. 14 | 15 | ### Thoroughly tested 16 | 17 | As of last count, the library is validated against 199,252 test cases. 18 | 19 | * **ExcelFinancialFunctions.Tests**: Unit tests checking against previously-determined truth values from Excel 2010. Inputs and expected outputs are read from data files. 20 | * **ExcelFinancialFunctions.ConsoleTests**: Test cases comparing the library results directly to running Excel code via interop. These should be run on a Windows machine with Excel 2013 (or later) installed. 21 | 22 | ### You can help! 23 | 24 | Found a discrepency? [Open an Issue](https://github.com/fsprojects/ExcelFinancialFunctions/issues)! Or better yet, a [Pull Request](https://github.com/fsprojects/ExcelFinancialFunctions/pulls). 25 | 26 | ## Adding it to your project 27 | 28 | Excel Financial Functions is a .NET Standard 2.0 library, which you can add to any project 29 | based on a .NET implementation which [supports the standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard). This includes .NET Framework 4.6.1 or later, and .NET Core 2.0 or later. 30 | 31 | Simply add it from NuGet in the usual way: 32 | 33 | ``` 34 | PS> dotnet add package ExcelFinancialFunctions 35 | 36 | Determining projects to restore... 37 | info : Adding PackageReference for package 'ExcelFinancialFunctions' into project 38 | info : GET https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 39 | info : OK https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 69ms 40 | info : Restoring packages for project.csproj... 41 | info : PackageReference for package 'ExcelFinancialFunctions' version '2.4.1' added to file 'project.csproj'. 42 | info : Committing restore... 43 | log : Restored project.csproj (in 72 ms). 44 | ``` 45 | 46 | ## Using it 47 | 48 | Even though the libary is written in F#, you can use it from any .NET language, including C#. The functions are provided as static methods on a Financial class in the Excel.FinancialFunctions namespace. 49 | 50 | ``` c# 51 | using Excel.FinancialFunctions; 52 | 53 | Console.WriteLine( Financial.IPmt(rate: 0.005, per: 53, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) ); 54 | // Displays -796.3747578439793 55 | 56 | Console.WriteLine( Financial.Pmt(rate: 0.005, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) ); 57 | // Displays -1687.7136560969248 58 | ``` 59 | 60 | Or from F#: 61 | 62 | ```F# 63 | open Excel.FinancialFunctions 64 | 65 | printfn "%f" <| Financial.IPmt (0.005, 53., 180., 200000., 0., PaymentDue.EndOfPeriod) 66 | // Displays -796.374758 67 | 68 | printfn "%f" <| Financial.Pmt (0.005, 180., 200000., 0., PaymentDue.EndOfPeriod) 69 | // Displays -1687.713656 70 | ``` 71 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/syd.test: -------------------------------------------------------------------------------- 1 | 100,10,1,0.3,153 2 | 100,10,1,1,90 3 | 100,10,13,0.3,13.54945054945 4 | 100,10,13,1,12.85714285714 5 | 100,10,13,1.7,12.16483516484 6 | 100,10,13,2,11.86813186813 7 | 100,10,13,10,3.956043956044 8 | 100,10,13,11.3,2.67032967033 9 | 100,10,13,13,0.989010989011 10 | 100,10,12.7,0.3,13.86286568194 11 | 100,10,12.7,1,13.13868613139 12 | 100,10,12.7,1.7,12.41450658084 13 | 100,10,12.7,2,12.10414391632 14 | 100,10,12.7,10,3.827806195758 15 | 100,10,12.7,11.3,2.482901316168 16 | 100,10,40,0.3,4.467073170732 17 | 100,10,40,1,4.390243902439 18 | 100,10,40,1.7,4.313414634146 19 | 100,10,40,2,4.280487804878 20 | 100,10,40,10,3.40243902439 21 | 100,10,40,11.3,3.259756097561 22 | 100,10,40,13,3.073170731707 23 | 100,50,1,0.3,85 24 | 100,50,1,1,50 25 | 100,50,13,0.3,7.527472527473 26 | 100,50,13,1,7.142857142857 27 | 100,50,13,1.7,6.758241758242 28 | 100,50,13,2,6.593406593407 29 | 100,50,13,10,2.197802197802 30 | 100,50,13,11.3,1.483516483516 31 | 100,50,13,13,0.5494505494506 32 | 100,50,12.7,0.3,7.70159204552 33 | 100,50,12.7,1,7.299270072993 34 | 100,50,12.7,1.7,6.896948100466 35 | 100,50,12.7,2,6.724524397954 36 | 100,50,12.7,10,2.126558997644 37 | 100,50,12.7,11.3,1.379389620093 38 | 100,50,40,0.3,2.481707317073 39 | 100,50,40,1,2.439024390244 40 | 100,50,40,1.7,2.396341463415 41 | 100,50,40,2,2.378048780488 42 | 100,50,40,10,1.890243902439 43 | 100,50,40,11.3,1.810975609756 44 | 100,50,40,13,1.707317073171 45 | 100,0,1,0.3,170 46 | 100,0,1,1,100 47 | 100,0,13,0.3,15.05494505495 48 | 100,0,13,1,14.28571428571 49 | 100,0,13,1.7,13.51648351648 50 | 100,0,13,2,13.18681318681 51 | 100,0,13,10,4.395604395604 52 | 100,0,13,11.3,2.967032967033 53 | 100,0,13,13,1.098901098901 54 | 100,0,12.7,0.3,15.40318409104 55 | 100,0,12.7,1,14.59854014599 56 | 100,0,12.7,1.7,13.79389620093 57 | 100,0,12.7,2,13.44904879591 58 | 100,0,12.7,10,4.253117995287 59 | 100,0,12.7,11.3,2.758779240186 60 | 100,0,40,0.3,4.963414634146 61 | 100,0,40,1,4.878048780488 62 | 100,0,40,1.7,4.792682926829 63 | 100,0,40,2,4.756097560976 64 | 100,0,40,10,3.780487804878 65 | 100,0,40,11.3,3.621951219512 66 | 100,0,40,13,3.414634146341 67 | 200,10,1,0.3,323 68 | 200,10,1,1,190 69 | 200,10,13,0.3,28.6043956044 70 | 200,10,13,1,27.14285714286 71 | 200,10,13,1.7,25.68131868132 72 | 200,10,13,2,25.05494505495 73 | 200,10,13,10,8.351648351648 74 | 200,10,13,11.3,5.637362637363 75 | 200,10,13,13,2.087912087912 76 | 200,10,12.7,0.3,29.26604977298 77 | 200,10,12.7,1,27.73722627737 78 | 200,10,12.7,1.7,26.20840278177 79 | 200,10,12.7,2,25.55319271222 80 | 200,10,12.7,10,8.080924191045 81 | 200,10,12.7,11.3,5.241680556354 82 | 200,10,40,0.3,9.430487804878 83 | 200,10,40,1,9.268292682927 84 | 200,10,40,1.7,9.106097560976 85 | 200,10,40,2,9.036585365854 86 | 200,10,40,10,7.182926829268 87 | 200,10,40,11.3,6.881707317073 88 | 200,10,40,13,6.487804878049 89 | 200,50,1,0.3,255 90 | 200,50,1,1,150 91 | 200,50,13,0.3,22.58241758242 92 | 200,50,13,1,21.42857142857 93 | 200,50,13,1.7,20.27472527473 94 | 200,50,13,2,19.78021978022 95 | 200,50,13,10,6.593406593407 96 | 200,50,13,11.3,4.450549450549 97 | 200,50,13,13,1.648351648352 98 | 200,50,12.7,0.3,23.10477613656 99 | 200,50,12.7,1,21.89781021898 100 | 200,50,12.7,1.7,20.6908443014 101 | 200,50,12.7,2,20.17357319386 102 | 200,50,12.7,10,6.379676992931 103 | 200,50,12.7,11.3,4.138168860279 104 | 200,50,40,0.3,7.44512195122 105 | 200,50,40,1,7.317073170732 106 | 200,50,40,1.7,7.189024390244 107 | 200,50,40,2,7.134146341463 108 | 200,50,40,10,5.670731707317 109 | 200,50,40,11.3,5.432926829268 110 | 200,50,40,13,5.121951219512 111 | 200,0,1,0.3,340 112 | 200,0,1,1,200 113 | 200,0,13,0.3,30.10989010989 114 | 200,0,13,1,28.57142857143 115 | 200,0,13,1.7,27.03296703297 116 | 200,0,13,2,26.37362637363 117 | 200,0,13,10,8.791208791209 118 | 200,0,13,11.3,5.934065934066 119 | 200,0,13,13,2.197802197802 120 | 200,0,12.7,0.3,30.80636818208 121 | 200,0,12.7,1,29.19708029197 122 | 200,0,12.7,1.7,27.58779240186 123 | 200,0,12.7,2,26.89809759182 124 | 200,0,12.7,10,8.506235990574 125 | 200,0,12.7,11.3,5.517558480372 126 | 200,0,40,0.3,9.926829268293 127 | 200,0,40,1,9.756097560976 128 | 200,0,40,1.7,9.585365853659 129 | 200,0,40,2,9.512195121951 130 | 200,0,40,10,7.560975609756 131 | 200,0,40,11.3,7.243902439024 132 | 200,0,40,13,6.829268292683 133 | 130,10,10,4,15.27272727273 134 | 130,10,10,4,15.27272727273 135 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/tbillyield.test: -------------------------------------------------------------------------------- 1 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,75,4.137931034483 2 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,100,0 3 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,130,-2.864721485411 4 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,75,2.666666666667 5 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,100,0 6 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,130,-1.846153846154 7 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,75,1.621621621622 8 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,100,0 9 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,130,-1.122661122661 10 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,75,0.6315789473684 11 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,100,0 12 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,130,-0.4372469635628 13 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,75,0.5479452054795 14 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,100,0 15 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,130,-0.3793466807165 16 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,75,7.5 17 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,100,0 18 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,130,-5.192307692308 19 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,75,2.666666666667 20 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,100,0 21 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,130,-1.846153846154 22 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,75,0.7453416149068 23 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,100,0 24 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,130,-0.516005733397 25 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,75,0.6315789473684 26 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,100,0 27 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,130,-0.4372469635628 28 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,75,2.666666666667 29 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,100,0 30 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,130,-1.846153846154 31 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,75,0.6315789473684 32 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,100,0 33 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,130,-0.4372469635628 34 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,75,2.666666666667 35 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,100,0 36 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,130,-1.846153846154 37 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,75,0.6315789473684 38 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,100,0 39 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,130,-0.4372469635628 40 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,75,2.666666666667 41 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,100,0 42 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,130,-1.846153846154 43 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,75,0.6315789473684 44 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,100,0 45 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,130,-0.4372469635628 46 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,75,0.3921568627451 47 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,100,0 48 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,130,-0.2714932126697 49 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,75,0.3418803418803 50 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,100,0 51 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,130,-0.2366863905325 52 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,75,2.666666666667 53 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,100,0 54 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,130,-1.846153846154 55 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,75,0.6315789473684 56 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,100,0 57 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,130,-0.4372469635628 58 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,75,2.666666666667 59 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,100,0 60 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,130,-1.846153846154 61 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,75,0.6315789473684 62 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,100,0 63 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,130,-0.4372469635628 64 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,75,2.666666666667 65 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,100,0 66 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,130,-1.846153846154 67 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,75,0.6315789473684 68 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,100,0 69 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,130,-0.4372469635628 70 | 2/28/2008 12:00:00 AM,2/27/2009 12:00:00 AM,0.25,393.5342465753 71 | 2/28/2008 12:00:00 AM,2/27/2009 12:00:00 AM,0.25,393.5342465753 72 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/tvm.fs: -------------------------------------------------------------------------------- 1 | // Time value of money routines, note the extensive treatment of error condition to help the user with sensible error messages 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | 8 | module internal Tvm = 9 | 10 | // Main formulas 11 | let fvFactor r nper = (1. + r) ** nper 12 | let pvFactor r nper = 1. / fvFactor r nper 13 | let annuityCertainPvFactor r nper (pd:PaymentDue) = if r = 0. then nper else (1. + r * float pd) * (1. - pvFactor r nper) / r 14 | let annuityCertainFvFactor r nper (pd:PaymentDue) = annuityCertainPvFactor r nper pd * fvFactor r nper 15 | let nperFactor r pmt v (pd:PaymentDue) = v * r + pmt * ( 1. + r * float pd ) 16 | 17 | let pv r nper pmt fv pd = - (fv * pvFactor r nper + pmt * annuityCertainPvFactor r nper pd) 18 | let fv r nper pmt pv pd = - (pv * fvFactor r nper + pmt * annuityCertainFvFactor r nper pd) 19 | let pmt r nper pv fv pd = - (pv + fv * pvFactor r nper) / annuityCertainPvFactor r nper pd 20 | let nper r pmt pv fv (pd:PaymentDue) = ln ( nperFactor r pmt (-fv) pd / nperFactor r pmt pv pd ) / ln (r+1.) 21 | 22 | // Preconditions and special cases 23 | let calcPv r nper pmt fv pd = 24 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is less than -1 and nper not an integer" 25 | ( pmt <> 0. || fv <> 0. ) |> elseThrow "pmt or fv need to be different from 0" 26 | ( r <> -1.) |> elseThrow "r cannot be -100%" 27 | pv r nper pmt fv pd 28 | let calcFv r nper pmt pv pd = 29 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer" 30 | ( r <> -1. || (r = -1. && nper > 0.) ) |> elseThrow "r cannot be -100% when nper is <= 0" 31 | if r = -1. && pd = PaymentDue.BeginningOfPeriod then - (pv * fvFactor r nper) 32 | elif r = -1. && pd = PaymentDue.EndOfPeriod then - (pv * fvFactor r nper + pmt) 33 | else fv r nper pmt pv pd 34 | let calcPmt r nper pv fv pd = 35 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer" 36 | ( r <> -1. || (r = -1. && nper > 0. && pd = PaymentDue.EndOfPeriod) ) |> elseThrow "r cannot be -100% when nper is <= 0" 37 | ( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0" 38 | if r = -1. then -fv 39 | else pmt r nper pv fv pd 40 | let calcNper r pmt pv fv pd = 41 | if r = 0. && pmt <> 0. then 42 | - (fv + pv) / pmt 43 | else 44 | nper r pmt pv fv pd 45 | let calcRri nper pv fv = 46 | ( nper > 0. ) |> elseThrow "nper must be > 0" 47 | if fv = pv then 0. 48 | else 49 | ( pv <> 0. ) |> elseThrow "pv must be non-zero unless fv is zero" 50 | ( fv/pv >= 0. ) |> elseThrow "fv and pv must have same sign" 51 | ( pow (fv/pv) (1.0/nper) ) - 1. 52 | let calcRate nper pmt pv fv pd guess = 53 | let haveRightSigns x y z = 54 | not( sign x = sign y && sign y = sign z) && 55 | not (sign x = sign y && z = 0.) && 56 | not (sign x = sign z && y = 0.) && 57 | not (sign y = sign z && x = 0.) 58 | 59 | ( pmt <> 0. || pv <> 0. ) |> elseThrow "pmt or pv need to be different from 0" 60 | ( nper > 0.) |> elseThrow "nper needs to be more than 0" 61 | ( haveRightSigns pmt pv fv ) |> elseThrow "There must be at least a change in sign in pv, fv and pmt" 62 | 63 | if fv = 0. && pv = 0. then 64 | if pmt < 0. then -1. else 1. 65 | else 66 | let f = fun r -> calcFv r nper pmt pv pd - fv 67 | findRoot f guess 68 | let calcFvSchedule (pv:float) interests = 69 | let mutable result = pv 70 | for i in interests do result <- result * (1. + i) 71 | result 72 | let calcPduration rate pv fv = 73 | ( rate > 0. ) |> elseThrow "rate must be positive" 74 | ( pv > 0. ) |> elseThrow "pv must be positive" 75 | ( fv > 0. ) |> elseThrow "fv must be positive" 76 | ( (log fv) - (log pv) ) / log ( 1. + rate) -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/spottests.fs: -------------------------------------------------------------------------------- 1 | namespace Excel.FinancialFunctions.Tests 2 | 3 | open FsCheck 4 | open NUnit.Framework 5 | 6 | [] 7 | [] 8 | module SpotTests = 9 | open System 10 | open Excel.FinancialFunctions 11 | open TestPreconditions 12 | 13 | [] 14 | let Readme1() = 15 | Financial.IPmt (0.005, 53., 180., 200000., 0., PaymentDue.EndOfPeriod) 16 | 17 | [] 18 | let Readme2() = 19 | Financial.Pmt (0.005, 180., 200000., 0., PaymentDue.EndOfPeriod) 20 | 21 | [] 22 | let FvPull67() = 23 | Financial.Fv (0.005, 180., 0., 0., PaymentDue.EndOfPeriod) 24 | 25 | [] 26 | let PmtPull67() = 27 | Financial.Pmt (0.005, 180., 0., 0., PaymentDue.EndOfPeriod) 28 | 29 | [] 30 | let YieldIssue8() = 31 | let param = DateTime(2015,9,21), DateTime(2015,10,15), 0.04625, 105.124, 100. , Frequency.SemiAnnual, DayCountBasis.UsPsa30_360 32 | Financial.Yield param 33 | |> shouldEqual (sprintf "YieldIssue8(%A)" param) -0.67428578540657702 34 | 35 | [] 36 | let XirrIssue27() = 37 | let values = [ -177900000.; 8799805.85 ] 38 | let dates = [ DateTime(2020,7,3); DateTime(2021,2,25) ] 39 | Financial.XIrr (values, dates) 40 | |> shouldEqual (sprintf "XirrIssue27(%A,%A)" values dates) -0.990247691899517 41 | 42 | [] 43 | let spotYield() = 44 | let param = 45 | DateTime(2008, 2, 15), DateTime(2016, 11, 15), 0.0575, 95.04287, 100.0, 46 | Frequency.SemiAnnual, DayCountBasis.UsPsa30_360 47 | Financial.Yield param 48 | |> shouldEqual (sprintf "spotYield(%A)" param) 0.065 49 | 50 | [] 51 | let spotXnpv() = 52 | let param = 0.14, [1.;3.;4.], [DateTime(1970, 3, 2); DateTime(1988, 2, 3); DateTime(1999, 3, 5)] 53 | Financial.XNpv param 54 | |> shouldEqual (sprintf "xnpv(%A)" param) 1.375214 55 | 56 | [] 57 | [] 58 | let ``duration shouldn't be greater than maturity``() = 59 | fsCheck (fun (sd: DateTime) yrs cpn' yld' freq basis -> 60 | let md, cpn, yld = sd.AddYears yrs, toFloat cpn', toFloat yld' 61 | 62 | tryDuration sd md cpn yld freq basis 63 | ==> 64 | lazy (let duration = Financial.Duration(sd, md, cpn, yld, freq, basis) 65 | duration - float yrs < PRECISION)) 66 | 67 | [] 68 | [] 69 | let ``mduration shouldn't be greater than maturity``() = 70 | fsCheck (fun (sd: DateTime) yrs cpn' yld' freq basis -> 71 | let md, cpn, yld = sd.AddYears yrs, toFloat cpn', toFloat yld' 72 | 73 | tryMDuration sd md cpn yld freq basis 74 | ==> 75 | lazy (let duration = Financial.MDuration(sd, md, cpn, yld, freq, basis) 76 | duration - float yrs < PRECISION)) 77 | 78 | [] 79 | let ``tbill price is less than 100``() = 80 | fsCheck (fun (sd: DateTime) t disc' -> 81 | let md, disc = sd.AddDays (toFloat t * 365.), toFloat disc' 82 | 83 | tryTBillPrice sd md disc 84 | ==> 85 | lazy (Financial.TBillPrice(sd, md, disc) - 100. < PRECISION)) 86 | 87 | [] 88 | let ``vdb in the period should be the same as the sum by subperiods``() = 89 | fsCheck (fun c s len switchToStraightLine -> 90 | let start, life = 0., 10. 91 | let cost, salvage, period = toFloat c * 1000., toFloat s * 100., toFloat len 92 | let p1, p2 = period, period * 2. 93 | 94 | tryVdb cost salvage life start p2 1. switchToStraightLine 95 | ==> 96 | lazy (let inline vdb sd ed = Financial.Vdb(cost, salvage, life, sd, ed, 1., switchToStraightLine) 97 | abs (vdb start p1 + vdb p1 p2 - vdb start p2) < PRECISION)) 98 | 99 | [] 100 | let ``cumulative accrint should be the same as the sum of payments``() = 101 | fsCheck (fun (issue: DateTime) p r freq basis frac -> 102 | let par, rate = toFloat p * 1000000., toFloat r 103 | let fd, md = issue.AddDays 30., issue.AddYears 10 104 | let ncd = Financial.CoupNCD(fd, md, freq, basis) 105 | 106 | tryAccrInt issue ncd fd rate par freq basis 107 | ==> 108 | lazy (let inline accrint interest settlement = 109 | Financial.AccrInt(issue, interest, settlement, rate, par, freq, basis) 110 | 111 | let daysTillInterest = max (360. / float freq * toFloat frac) 1. 112 | let interest = ncd.AddDays daysTillInterest 113 | abs (accrint ncd fd + accrint interest ncd - accrint interest fd) < PRECISION)) 114 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/tbillprice.test: -------------------------------------------------------------------------------- 1 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.01,99.91944444444 2 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.25,97.98611111111 3 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.75,93.95833333333 4 | 2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,2,83.88888888889 5 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.01,99.875 6 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.25,96.875 7 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.75,90.625 8 | 2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,2,75 9 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.01,99.79444444444 10 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.25,94.86111111111 11 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.75,84.58333333333 12 | 2/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,2,58.88888888889 13 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.01,99.47222222222 14 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.25,86.80555555556 15 | 2/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.75,60.41666666667 16 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.01,99.39166666667 17 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.25,84.79166666667 18 | 2/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.75,54.375 19 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.01,99.95555555556 20 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.25,98.88888888889 21 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.75,96.66666666667 22 | 3/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,2,91.11111111111 23 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.01,99.875 24 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.25,96.875 25 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,0.75,90.625 26 | 3/15/1980 12:00:00 AM,4/29/1980 12:00:00 AM,2,75 27 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.01,99.55277777778 28 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.25,88.81944444444 29 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,0.75,66.45833333333 30 | 3/15/1980 12:00:00 AM,8/23/1980 12:00:00 AM,2,10.55555555556 31 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.01,99.47222222222 32 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.25,86.80555555556 33 | 3/15/1980 12:00:00 AM,9/21/1980 12:00:00 AM,0.75,60.41666666667 34 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.01,99.875 35 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.25,96.875 36 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.75,90.625 37 | 12/31/1993 12:00:00 AM,2/14/1994 12:00:00 AM,2,75 38 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.01,99.47222222222 39 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.25,86.80555555556 40 | 12/31/1993 12:00:00 AM,7/9/1994 12:00:00 AM,0.75,60.41666666667 41 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,0.01,99.875 42 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,0.25,96.875 43 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,0.75,90.625 44 | 2/14/2003 12:00:00 AM,3/31/2003 12:00:00 AM,2,75 45 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.01,99.47222222222 46 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.25,86.80555555556 47 | 2/14/2003 12:00:00 AM,8/23/2003 12:00:00 AM,0.75,60.41666666667 48 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,0.01,99.875 49 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,0.25,96.875 50 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,0.75,90.625 51 | 10/31/2007 12:00:00 AM,12/15/2007 12:00:00 AM,2,75 52 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.01,99.47222222222 53 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.25,86.80555555556 54 | 10/31/2007 12:00:00 AM,5/8/2008 12:00:00 AM,0.75,60.41666666667 55 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.01,99.15 56 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.25,78.75 57 | 2/28/1993 12:00:00 AM,12/31/1993 12:00:00 AM,0.75,36.25 58 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.01,99.025 59 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.25,75.625 60 | 2/28/1993 12:00:00 AM,2/14/1994 12:00:00 AM,0.75,26.875 61 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,0.01,99.875 62 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,0.25,96.875 63 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,0.75,90.625 64 | 2/28/1993 12:00:00 AM,4/14/1993 12:00:00 AM,2,75 65 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.01,99.47222222222 66 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.25,86.80555555556 67 | 2/28/1993 12:00:00 AM,9/6/1993 12:00:00 AM,0.75,60.41666666667 68 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,0.01,99.875 69 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,0.25,96.875 70 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,0.75,90.625 71 | 3/31/1981 12:00:00 AM,5/15/1981 12:00:00 AM,2,75 72 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.01,99.47222222222 73 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.25,86.80555555556 74 | 3/31/1981 12:00:00 AM,10/7/1981 12:00:00 AM,0.75,60.41666666667 75 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,0.01,99.875 76 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,0.25,96.875 77 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,0.75,90.625 78 | 3/31/2004 12:00:00 AM,5/15/2004 12:00:00 AM,2,75 79 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.01,99.47222222222 80 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.25,86.80555555556 81 | 3/31/2004 12:00:00 AM,10/7/2004 12:00:00 AM,0.75,60.41666666667 82 | 2/29/2008 12:00:00 AM,2/27/2009 12:00:00 AM,0.25,74.72222222222 83 | 2/29/2008 12:00:00 AM,2/27/2009 12:00:00 AM,0.25,74.72222222222 84 | -------------------------------------------------------------------------------- /docs/compatibility.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | #I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0" 3 | #r "ExcelFinancialFunctions.dll" 4 | 5 | open System 6 | open Excel.FinancialFunctions 7 | 8 | (** 9 | Compatibility 10 | ============= 11 | 12 | This library replicates Excel behavior. There're 199,252 tests verifying the results against Excel 2010 and their number can be raised significantly by adding new test values. Several tests check the function properties, e.g. that bond duration can't be greater than maturity. 13 | The current version matches Excel 2010, which is slightly different from 2003, see [Function Improvements in Excel 2010](http://blogs.office.com/b/microsoft-excel/archive/2009/09/10/function-improvements-in-excel-2010.aspx). 14 | Note, that console tests require Excel, whereas the unit tests can be run even on mono - their parameters and expected results are stored in files. 15 | 16 | 17 | However, there're still some differences comparing to Excel. 18 | _Read more about OpenOffice vs Excel [here](openofficediff.html)._ 19 | 20 | 21 | COUPDAYS 22 | -------- 23 | 24 | The Excel algorithm doesn't respect equality `coupDays = coupDaysBS + coupDaysNC`. The library result differs from Excel by +/- one or two days when the date spans a leap year. ([office docs](http://office.microsoft.com/en-us/excel/HP052090311033.aspx)) 25 | *) 26 | 27 | let settlement = DateTime(2012, 1, 1) 28 | let maturity = DateTime(2016, 2, 29) 29 | 30 | let param = settlement, maturity, Frequency.SemiAnnual, DayCountBasis.ActualActual 31 | 32 | let days = Financial.CoupDays param 33 | let bs = Financial.CoupDaysBS param 34 | let nc = Financial.CoupDaysNC param 35 | // Excel: 2 36 | days - bs - nc 37 | // [fsi:val days : float = 182.0] 38 | // [fsi:val bs : float = 123.0] 39 | // [fsi:val nc : float = 59.0] 40 | // [fsi:val it : float = 0.0] 41 | 42 | 43 | (** 44 | VDB 45 | --- 46 | 47 | In the Excel version of this algorithm the depreciation in the period (0,1) is not the same as the sum of the depreciations in periods (0,0.5) (0.5,1) 48 | Notice that in Excel by using '1' (no_switch) instead of '0' as the last parameter everything works as expected. 49 | In truth, the last parameter should have no influence in the calculation given that in the first period there is no switch to sln depreciation. 50 | Overall, the algorithm is correct, even if it disagrees with Excel when startperiod is fractional. ([office docs](http://office.microsoft.com/en-us/excel/HP052093341033.aspx)) 51 | *) 52 | 53 | let vdb sp ep switch = 54 | Financial.Vdb(100.0, 10.0, 13.0, sp, ep, 1.0, 55 | if switch then VdbSwitch.SwitchToStraightLine else VdbSwitch.DontSwitchToStraightLine) 56 | 57 | let p1 = vdb 0.0 0.5 false 58 | let p2 = vdb 0.5 1.0 false 59 | let total = vdb 0.0 1.0 false 60 | // Excel: 0.1479 61 | total - p1 - p2 62 | // [fsi:val p1 : float = 3.846153846] 63 | // [fsi:val p2 : float = 3.846153846] 64 | // [fsi:val total : float = 7.692307692] 65 | // [fsi:val it : float = 0.0] 66 | 67 | let p1sw = vdb 0.0 0.5 true 68 | let p2sw = vdb 0.5 1.0 true 69 | let totalsw = vdb 0.0 1.0 true 70 | // Excel: 0.0000 71 | totalsw - p1sw - p2sw 72 | // [fsi:val p1sw : float = 3.846153846] 73 | // [fsi:val p2sw : float = 3.846153846] 74 | // [fsi:val totalsw : float = 7.692307692] 75 | // [fsi:val it : float = 0.0] 76 | 77 | 78 | (** 79 | AMORDEGRC 80 | --------- 81 | 82 | ExcelCompliant is used because Excel stores 13 digits. AmorDegrc algorithm rounds numbers 83 | and returns different results unless the numbers get rounded to 13 digits before rounding them. 84 | I.E. 22.49999999999999 is considered 22.5 by Excel, but 22.4 by the .NET framework. ([office docs](http://office.microsoft.com/en-us/excel/HP052089841033.aspx)) 85 | *) 86 | 87 | let amorDegrc excelCompliant = 88 | Financial.AmorDegrc(100.0, DateTime(2014,1,1), DateTime(2016,1,1), 89 | 50.0, 1.0, 0.3, DayCountBasis.ActualActual, excelCompliant) 90 | 91 | amorDegrc true 92 | // [fsi:val it : float = 23.0] 93 | amorDegrc false 94 | // [fsi:val it : float = 22.0] 95 | 96 | 97 | (** 98 | DDB 99 | --- 100 | 101 | Excel Ddb has two interesting characteristics: 102 | 1. It special cases ddb for fractional periods between 0 and 1 by considering them to be 1 103 | 2. It is inconsistent with VDB(..., True) for fractional periods, even if VDB(..., True) is defined to be the same as ddb. The algorithm for VDB is theoretically correct. 104 | This function makes the same 1. adjustment.([office docs](http://office.microsoft.com/en-us/excel/HP052090511033.aspx)) 105 | *) 106 | 107 | 108 | (** 109 | RATE and ODDFYIELD 110 | ------------------ 111 | 112 | Excel uses a different root finding algo. Sometimes the library results are better, sometimes Excel's. ([office docs](http://office.microsoft.com/en-us/excel/HP052092321033.aspx)) 113 | *) 114 | 115 | (** 116 | XIRR and XNPV 117 | ------------- 118 | 119 | XIRR and XNPV functions are related: the net present value, given the internal rate of return, should be zero. 120 | However, XNPV works only for positive rates even though the XIRR results might be negative. 121 | The results can also be different because of the root finding functions. ([office docs](http://office.microsoft.com/en-us/excel/HP052093411033.aspx)) 122 | *) 123 | 124 | let dates = [|DateTime(2000, 2, 29); DateTime(2000, 3, 31)|] 125 | let values = [|206101714.849377; -156650972.54265|] 126 | // Excel: -0.960452189 127 | Financial.XIrr(values, dates, -0.1) 128 | // [fsi:val it : float = -0.960452195] 129 | // Excel: #NUM! 130 | Financial.XNpv(-0.960452195, values, dates) 131 | // [fsi:val it : float = -0.008917063475] 132 | // Excel: #NUM! 133 | Financial.XNpv(-0.960452189, values, dates) 134 | // [fsi:val it : float = 2.646784514] 135 | 136 | let values2 = [|15108163.3840923; -75382259.6628424|] 137 | // Excel: #NUM! 138 | Financial.XIrr(values2, dates, -0.1) 139 | // [fsi:val it : float = 165601346.1] 140 | // Excel: 165601345.6 141 | Financial.XIrr(values2, dates, 0.1) 142 | // [fsi:val it : float = 165601346.1] 143 | Financial.XNpv(165601346.1, values2, dates) 144 | // [fsi:val it : float = -0.000269997865] 145 | Financial.XNpv(165601345.6, values2, dates) 146 | // [fsi:val it : float = -0.004144238308] 147 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/loan.fs: -------------------------------------------------------------------------------- 1 | // Loan related calculation routines, small variations on TVM 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | open Excel.FinancialFunctions.Tvm 8 | 9 | module internal Loan = 10 | let inline approxEqual x y = abs (x - y) < 1e-10 11 | 12 | // Main formulas 13 | let ipmt r per nper pv fv pd = 14 | let result = - ( pv * fvFactor r (per - 1.) * r + (pmt r nper pv fv PaymentDue.EndOfPeriod) * (fvFactor r (per - 1.) - 1.) ) 15 | if pd = PaymentDue.EndOfPeriod then result else result / (1. + r) 16 | let ppmt r per nper pv fv pd = 17 | pmt r nper pv fv pd - ipmt r per nper pv fv pd 18 | let ispmt r per nper pv = 19 | let coupon = - pv * r 20 | coupon - (coupon / nper * per) 21 | 22 | // Preconditions and special cases 23 | let calcIpmt r per nper pv fv pd = 24 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)" 25 | ( raisable r (per - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)" 26 | ( fv <> 0. || pv <> 0. ) |> elseThrow "fv or pv need to be different from 0" 27 | ( r > -1.) |> elseThrow "r must be more than -100%" 28 | ( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0" 29 | ( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper" 30 | ( nper > 0. ) |> elseThrow "nper must be more than 0" 31 | if approxEqual per 1. && pd = PaymentDue.BeginningOfPeriod then 0. 32 | elif r = -1. then -fv 33 | else ipmt r per nper pv fv pd 34 | let calcPpmt r per nper pv fv pd = 35 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)" 36 | ( raisable r (per - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)" 37 | ( fv <> 0. || pv <> 0. ) |> elseThrow "fv or pv need to be different from 0" 38 | ( r > -1. ) |> elseThrow "r must be more than -100%" 39 | ( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0" 40 | ( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper" 41 | ( nper > 0. ) |> elseThrow "nper must be more than 0" 42 | if approxEqual per 1. && pd = PaymentDue.BeginningOfPeriod then pmt r nper pv fv pd 43 | elif r = -1. then 0. 44 | else ppmt r per nper pv fv pd 45 | let calcCumipmt r nper pv startPeriod endPeriod pd = 46 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)" 47 | ( raisable r (startPeriod - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)" 48 | ( pv > 0. ) |> elseThrow "pv must be more than 0" 49 | ( r > 0. ) |> elseThrow "r must be more than 0" 50 | ( nper > 0. ) |> elseThrow "nper must be more than 0" 51 | ( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0" 52 | ( startPeriod <= endPeriod ) |> elseThrow "startPeriod must be less or equal to endPeriod" 53 | ( startPeriod >= 1. ) |> elseThrow "startPeriod must be more or equal to 1" 54 | ( endPeriod <= nper ) |> elseThrow "startPeriod and endPeriod must be less or equal to nper" 55 | aggrBetween (int (ceiling startPeriod)) (int endPeriod) (fun acc per -> acc + calcIpmt r (float per) nper pv 0. pd) 0. 56 | let calcCumprinc r nper pv startPeriod endPeriod pd = 57 | ( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)" 58 | ( raisable r (startPeriod - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)" 59 | ( pv > 0. ) |> elseThrow "pv must be more than 0" 60 | ( r > 0. ) |> elseThrow "r must be more than 0" 61 | ( nper > 0. ) |> elseThrow "nper must be more than 0" 62 | ( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0" 63 | ( startPeriod <= endPeriod ) |> elseThrow "startPeriod must be less or equal to endPeriod" 64 | ( startPeriod >= 1. ) |> elseThrow "startPeriod must be more or equal to 1" 65 | ( endPeriod <= nper ) |> elseThrow "startPeriod and endPeriod must be less or equal to nper" 66 | aggrBetween (int (ceiling startPeriod)) (int endPeriod) (fun acc per -> acc + calcPpmt r (float per) nper pv 0. pd) 0. 67 | let calcIspmt r per nper pv = 68 | ( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper" 69 | ( nper > 0. ) |> elseThrow "nper must be more than 0" 70 | ispmt r per nper pv -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/mirr.test: -------------------------------------------------------------------------------- 1 | -100;10;10;100,-1.5,-1.5,-0.008403758659613 2 | -100;10;10;100,-1.5,-2,0 3 | -100;10;10;100,-1.5,-0.4,0.03102734709515 4 | -100;10;10;100,-1.5,-0.1,0.05402836501767 5 | -100;10;10;100,-1.5,0,0.06265856918261 6 | -100;10;10;100,-1.5,0.6,0.1229344816003 7 | -100;10;10;100,-1.5,1.5,0.2331060371652 8 | -100;10;10;100,-2,-1.5,-0.008403758659613 9 | -100;10;10;100,-2,-2,0 10 | -100;10;10;100,-2,-0.4,0.03102734709515 11 | -100;10;10;100,-2,-0.1,0.05402836501767 12 | -100;10;10;100,-2,0,0.06265856918261 13 | -100;10;10;100,-2,0.6,0.1229344816003 14 | -100;10;10;100,-2,1.5,0.2331060371652 15 | -100;10;10;100,-0.4,-1.5,-0.008403758659613 16 | -100;10;10;100,-0.4,-2,0 17 | -100;10;10;100,-0.4,-0.4,0.03102734709515 18 | -100;10;10;100,-0.4,-0.1,0.05402836501767 19 | -100;10;10;100,-0.4,0,0.06265856918261 20 | -100;10;10;100,-0.4,0.6,0.1229344816003 21 | -100;10;10;100,-0.4,1.5,0.2331060371652 22 | -100;10;10;100,-0.1,-1.5,-0.008403758659613 23 | -100;10;10;100,-0.1,-2,0 24 | -100;10;10;100,-0.1,-0.4,0.03102734709515 25 | -100;10;10;100,-0.1,-0.1,0.05402836501767 26 | -100;10;10;100,-0.1,0,0.06265856918261 27 | -100;10;10;100,-0.1,0.6,0.1229344816003 28 | -100;10;10;100,-0.1,1.5,0.2331060371652 29 | -100;10;10;100,0,-1.5,-0.008403758659613 30 | -100;10;10;100,0,-2,0 31 | -100;10;10;100,0,-0.4,0.03102734709515 32 | -100;10;10;100,0,-0.1,0.05402836501767 33 | -100;10;10;100,0,0,0.06265856918261 34 | -100;10;10;100,0,0.6,0.1229344816003 35 | -100;10;10;100,0,1.5,0.2331060371652 36 | -100;10;10;100,0.6,-1.5,-0.008403758659613 37 | -100;10;10;100,0.6,-2,0 38 | -100;10;10;100,0.6,-0.4,0.03102734709515 39 | -100;10;10;100,0.6,-0.1,0.05402836501767 40 | -100;10;10;100,0.6,0,0.06265856918261 41 | -100;10;10;100,0.6,0.6,0.1229344816003 42 | -100;10;10;100,0.6,1.5,0.2331060371652 43 | -100;10;10;100,1.5,-1.5,-0.008403758659613 44 | -100;10;10;100,1.5,-2,0 45 | -100;10;10;100,1.5,-0.4,0.03102734709515 46 | -100;10;10;100,1.5,-0.1,0.05402836501767 47 | -100;10;10;100,1.5,0,0.06265856918261 48 | -100;10;10;100,1.5,0.6,0.1229344816003 49 | -100;10;10;100,1.5,1.5,0.2331060371652 50 | -100;-10;10;100,-1.5,-1.5,0.05895589606372 51 | -100;-10;10;100,-1.5,-2,0.04004191152595 52 | -100;-10;10;100,-1.5,-0.4,0.09834461751387 53 | -100;-10;10;100,-1.5,-0.1,0.1086101154833 54 | -100;-10;10;100,-1.5,0,0.1119900452847 55 | -100;-10;10;100,-1.5,0.6,0.131851195963 56 | -100;-10;10;100,-1.5,1.5,0.1603972084032 57 | -100;-10;10;100,-2,-1.5,0.01818579071493 58 | -100;-10;10;100,-2,-2,0 59 | -100;-10;10;100,-2,-0.4,0.05605803510589 60 | -100;-10;10;100,-2,-0.1,0.06592830846284 61 | -100;-10;10;100,-2,0,0.06917810999861 62 | -100;-10;10;100,-2,0.6,0.08827460068633 63 | -100;-10;10;100,-2,1.5,0.1157215834703 64 | -100;-10;10;100,-0.4,-1.5,-0.06618910089637 65 | -100;-10;10;100,-0.4,-2,-0.08286787380136 66 | -100;-10;10;100,-0.4,-0.4,-0.03145524887418 67 | -100;-10;10;100,-0.4,-0.1,-0.02240290408416 68 | -100;-10;10;100,-0.4,0,-0.01942240669193 69 | -100;-10;10;100,-0.4,0.6,-0.001908401584573 70 | -100;-10;10;100,-0.4,1.5,0.02326410809381 71 | -100;-10;10;100,-0.1,-1.5,-0.05087800419707 72 | -100;-10;10;100,-0.1,-2,-0.06783024821384 73 | -100;-10;10;100,-0.1,-0.4,-0.01557464354357 74 | -100;-10;10;100,-0.1,-0.1,-0.006373873278356 75 | -100;-10;10;100,-0.1,0,-0.003344506587404 76 | -100;-10;10;100,-0.1,0.6,0.01445666439695 77 | -100;-10;10;100,-0.1,1.5,0.04004191152595 78 | -100;-10;10;100,0,-1.5,-0.04769300718637 79 | -100;-10;10;100,0,-2,-0.06470213835438 80 | -100;-10;10;100,0,-0.4,-0.01227117799179 81 | -100;-10;10;100,0,-0.1,-0.003039532427178 82 | -100;-10;10;100,0,0,0 83 | -100;-10;10;100,0,0.6,0.01786090690516 84 | -100;-10;10;100,0,1.5,0.04353201121162 85 | -100;-10;10;100,0.6,-1.5,-0.0366186775264 86 | -100;-10;10;100,0.6,-2,-0.05382560701702 87 | -100;-10;10;100,0.6,-0.4,-0.0007849296788888 88 | -100;-10;10;100,0.6,-0.1,0.008554070223199 89 | -100;-10;10;100,0.6,0,0.01162894921862 90 | -100;-10;10;100,0.6,0.6,0.02969755970318 91 | -100;-10;10;100,0.6,1.5,0.055667191978 92 | -100;-10;10;100,1.5,-1.5,-0.02972072431552 93 | -100;-10;10;100,1.5,-2,-0.04705085797438 94 | -100;-10;10;100,1.5,-0.4,0.006369598483423 95 | -100;-10;10;100,1.5,-0.1,0.01577546701049 96 | -100;-10;10;100,1.5,0,0.01887236259575 97 | -100;-10;10;100,1.5,0.6,0.03707034701231 98 | -100;-10;10;100,1.5,1.5,0.06322592570748 99 | -200;0;10;-10;300,-1.5,-1.5,0.2600449358144 100 | -200;0;10;-10;300,-1.5,-2,0.2677835488884 101 | -200;0;10;-10;300,-1.5,-0.4,0.2611888724766 102 | -200;0;10;-10;300,-1.5,-0.1,0.2658364965098 103 | -200;0;10;-10;300,-1.5,0,0.2677835488884 104 | -200;0;10;-10;300,-1.5,0.6,0.2834406414325 105 | -200;0;10;-10;300,-1.5,1.5,0.3183529375264 106 | -200;0;10;-10;300,-2,-1.5,0.1232927206602 107 | -200;0;10;-10;300,-2,-2,0.130191464893 108 | -200;0;10;-10;300,-2,-0.4,0.1243125062956 109 | -200;0;10;-10;300,-2,-0.1,0.1284557253956 110 | -200;0;10;-10;300,-2,0,0.130191464893 111 | -200;0;10;-10;300,-2,0.6,0.1441492989207 112 | -200;0;10;-10;300,-2,1.5,0.1752725763127 113 | -200;0;10;-10;300,-0.4,-1.5,0.05272969727662 114 | -200;0;10;-10;300,-0.4,-2,0.05919507606371 115 | -200;0;10;-10;300,-0.4,-0.4,0.05368542199869 116 | -200;0;10;-10;300,-0.4,-0.1,0.05756837228284 117 | -200;0;10;-10;300,-0.4,0,0.05919507606371 118 | -200;0;10;-10;300,-0.4,0.6,0.07227610660928 119 | -200;0;10;-10;300,-0.4,1.5,0.1014442813731 120 | -200;0;10;-10;300,-0.1,-1.5,0.09074031008576 121 | -200;0;10;-10;300,-0.1,-2,0.09743913247226 122 | -200;0;10;-10;300,-0.1,-0.4,0.09173054288949 123 | -200;0;10;-10;300,-0.1,-0.1,0.09575369375903 124 | -200;0;10;-10;300,-0.1,0,0.09743913247226 125 | -200;0;10;-10;300,-0.1,0.6,0.1109924760802 126 | -200;0;10;-10;300,-0.1,1.5,0.1412138178633 127 | -200;0;10;-10;300,0,-1.5,0.0955356663742 128 | -200;0;10;-10;300,0,-2,0.1022639396206 129 | -200;0;10;-10;300,0,-0.4,0.09653025266066 130 | -200;0;10;-10;300,0,-0.1,0.1005710910051 131 | -200;0;10;-10;300,0,0,0.1022639396206 132 | -200;0;10;-10;300,0,0.6,0.1158768694664 133 | -200;0;10;-10;300,0,1.5,0.1462310770654 134 | -200;0;10;-10;300,0.6,-1.5,0.105621583878 135 | -200;0;10;-10;300,0.6,-2,0.1124118001638 136 | -200;0;10;-10;300,0.6,-0.4,0.1066253267036 137 | -200;0;10;-10;300,0.6,-0.1,0.1107033665408 138 | -200;0;10;-10;300,0.6,0,0.1124118001638 139 | -200;0;10;-10;300,0.6,0.6,0.1261500558128 140 | -200;0;10;-10;300,0.6,1.5,0.1567837157776 141 | -200;0;10;-10;300,1.5,-1.5,0.1080949227489 142 | -200;0;10;-10;300,1.5,-2,0.1149003291378 143 | -200;0;10;-10;300,1.5,-0.4,0.1091009110048 144 | -200;0;10;-10;300,1.5,-0.1,0.1131880736509 145 | -200;0;10;-10;300,1.5,0,0.1149003291378 146 | -200;0;10;-10;300,1.5,0.6,0.1286693180524 147 | -200;0;10;-10;300,1.5,1.5,0.1593715072708 148 | -123;12;15;50;200,0.14,0.12,0.2409336872986 149 | -123;12;15;50;200,0.14,0.12,0.2409336872986 150 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Excel Financial Functions 2 | 3 | This is a .NET Standard library that provides the full set of financial functions from Excel. The main goal for the library is compatibility with Excel, by providing the same functions, with the same behaviour. Note though that this is not a wrapper over the Excel library; the functions have been re-implemented in managed code so that you do not need to have Excel installed to use this library. 4 | 5 | [![Build+Test+Docs](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml/badge.svg)](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml) 6 | [![NuGet Badge](https://img.shields.io/nuget/v/ExcelFinancialFunctions.svg?style=flat)](https://www.nuget.org/packages/ExcelFinancialFunctions/) 7 | 8 | ## Goal: Match Excel 9 | 10 | We replicate the results Excel would produce in every situation, 11 | even in cases where we might disagree with Excel\'s approach. Please have a look at the [Compatibility](http://fsprojects.github.io/ExcelFinancialFunctions/compatibility.html) page for more detail on this topic. 12 | 13 | Microsoft\'s official documentation on [Excel Functions](https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb) is the best place to learn more about how the functions should work. The scope for this library is the full set of functions in the "Financial Functions" category. 14 | 15 | ### Thoroughly tested 16 | 17 | As of last count, the library is validated against 199,252 test cases. 18 | 19 | * [ExcelFinancialFunctions.Tests](./tests/ExcelFinancialFunctions.Tests): Unit tests checking against previously-determined truth values from Excel 2010. Inputs and expected outputs are read from data files. 20 | * [ExcelFinancialFunctions.ConsoleTests](./tests/ExcelFinancialFunctions.ConsoleTests): Test cases comparing the library results directly to running Excel code via interop. These should be run on a Windows machine with Excel 2013 (or later) installed. 21 | 22 | ### Difference #1: CoupDays 23 | 24 | There are two notable areas where we judged that Excel was sufficiently incorrect 25 | such that we needed to deviate from the primary goal of matching Excel precisely. 26 | 27 | The first is the coupDays algorithm. Excel doesn't respect the following: 28 | 29 | ``` 30 | coupDays = coupDaysBS + coupDaysNC. 31 | ``` 32 | 33 | This equality should stand. The result differs from Excel by +/- one or two days when the date spans a leap year. 34 | 35 | ### Difference #2: VDB 36 | 37 | In the excel version of this algorithm the depreciation in the period (0,1) is not the same as 38 | the sum of the depreciations in periods (0,0.5) (0.5,1). 39 | 40 | ``` 41 | VDB(100,10,13,0,0.5,1,0) + VDB(100,10,13,0.5,1,1,0) <> VDB(100,10,13,0,1,1,0) 42 | ``` 43 | 44 | Notice that in Excel by using '1' (no_switch) instead of '0' as the last parameter everything works as expected. The last parameter should have no influence in the calculation given that in the first period there is no switch to sln depreciation. 45 | 46 | ### You can help! 47 | 48 | Found a discrepency? [Open an Issue](https://github.com/fsprojects/ExcelFinancialFunctions/issues)! Or better yet, a [Pull Request](https://github.com/fsprojects/ExcelFinancialFunctions/pulls). 49 | 50 | ## Adding it to your project 51 | 52 | Excel Financial Functions is a .NET Standard 2.0 library, which you can add to any project 53 | based on a .NET implementation which [supports the standard](https://docs.microsoft.com/en-us/dotnet/standard/net-standard). This includes .NET Framework 4.6.1 or later, and .NET Core 2.0 or later. 54 | 55 | Simply add it from NuGet in the usual way: 56 | 57 | ``` 58 | PS> dotnet add package ExcelFinancialFunctions 59 | 60 | Determining projects to restore... 61 | info : Adding PackageReference for package 'ExcelFinancialFunctions' into project 62 | info : GET https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 63 | info : OK https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 69ms 64 | info : Restoring packages for project.csproj... 65 | info : PackageReference for package 'ExcelFinancialFunctions' version '2.4.1' added to file 'project.csproj'. 66 | info : Committing restore... 67 | log : Restored project.csproj (in 72 ms). 68 | ``` 69 | 70 | ## Using it 71 | 72 | Even though the libary is written in F#, you can use it from any .NET language, including C#. The functions are provided as static methods on a Financial class in the Excel.FinancialFunctions namespace. 73 | 74 | ``` c# 75 | using Excel.FinancialFunctions; 76 | 77 | Console.WriteLine( Financial.IPmt(rate: 0.005, per: 53, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) ); 78 | // Displays -796.3747578439793 79 | 80 | Console.WriteLine( Financial.Pmt(rate: 0.005, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) ); 81 | // Displays -1687.7136560969248 82 | ``` 83 | 84 | Or from F#: 85 | 86 | ```F# 87 | open Excel.FinancialFunctions 88 | 89 | printfn "%f" <| Financial.IPmt (0.005, 53., 180., 200000., 0., PaymentDue.EndOfPeriod) 90 | // Displays -796.374758 91 | 92 | printfn "%f" <| Financial.Pmt (0.005, 180., 200000., 0., PaymentDue.EndOfPeriod) 93 | // Displays -1687.713656 94 | ``` 95 | 96 | ## Code of Conduct 97 | 98 | This repository is governed by the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/). 99 | 100 | We pledge to be overt in our openness, welcoming all people to contribute, and pledging in return to value them as whole human beings and to foster an atmosphere of kindness, cooperation, and understanding. 101 | 102 | ## Library license 103 | 104 | The library is available under Apache 2.0. For more information see the [License file](./LICENSE.txt). 105 | 106 | ## The origin 107 | 108 | The original author is [Luca Bolognese](https://github.com/lucabol). His story: 109 | 110 | > I coded it the summer my daughter was born 12 years ago while back in Italy for three months. It is one of my first forays into functional programming. 111 | > 112 | > I coded most of them with the sea in front of me. I would bet that it is one of the most Excel-compliant libraries in existence across all languages. 113 | > 114 | > I am happy that the tests still pass! This is a testament to good backward compatibility in the language. 115 | > 116 | > We think this is one of the most Excel compliant libraries in existence because of the extensive test-suite. 117 | 118 | ## Maintainers 119 | 120 | Current maintainer is [James Coliz](https://github.com/jcoliz). 121 | 122 | Historical maintainers of this project are [Natallie Baikevich](https://github.com/luajalla) and [Chris Pell](https://github.com/jcoliz). And of course, where would we be without [Don Syme](https://github.com/dsyme)? 123 | 124 | The default maintainer account for projects under "fsprojects" is [@fsprojectsgit](https://github.com/fsprojectsgit) - F# Community Project Incubation Space (repo management) 125 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/common.fs: -------------------------------------------------------------------------------- 1 | // Common math, error management, zero finding, etc... routines used in all the rest of the library 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open System.Collections.Generic 7 | 8 | module internal Common = 9 | 10 | // Error management functions 11 | let mutable precision = 0.0001 12 | let areEqual x y = if abs(x - y) < precision then true else false 13 | let throw s = failwith s 14 | let elseThrow s c = if not(c) then throw s 15 | let raisable b p = not( (1. + b) < 0. && (p - float (int p)) <> 0. ) 16 | // Mathematical functions 17 | let ln x = Math.Log(x) 18 | let sign (x:float) = Math.Sign(x) 19 | let idem x = x 20 | let min (x:float) y = Math.Min(x, y) 21 | let max (x:float) y = Math.Max(x, y) 22 | let rest x = x - float (int x) 23 | let ceiling (x:float) = Math.Ceiling(x) 24 | let floor (x:float) = Math.Floor(x) 25 | let pow x y = Math.Pow(x, y) 26 | let sqr x = Math.Sqrt(x) 27 | let log10 x = Math.Log(x, 10.) 28 | let round excelComplaint (x:float) = 29 | // Excel precision is of 13 digits so to be Excel compatible you need to preround to 13 digits ... 30 | if excelComplaint then 31 | let k = Math.Round(x, 13, MidpointRounding.AwayFromZero) 32 | Math.Round(k, MidpointRounding.AwayFromZero) 33 | else Math.Round(x, MidpointRounding.AwayFromZero) 34 | 35 | // Don't want to use fold directly as it is hard to read. Building simpler utility func instead ... 36 | let aggrBetween startPeriod endPeriod f initialValue= 37 | let s = if startPeriod <= endPeriod then {startPeriod .. 1 .. endPeriod} else {startPeriod .. -1 .. endPeriod} 38 | s |> Seq.fold f initialValue 39 | 40 | // Date functions 41 | let days (after:DateTime) (before:DateTime) = (after - before).Days 42 | let date y m d = new DateTime(y, m, d) 43 | let (|Date|) (d1:DateTime) = (d1.Year,d1.Month,d1.Day) 44 | let isLeapYear (Date(y,_,_) as d) = DateTime.IsLeapYear(y) 45 | let leapYear y = DateTime.IsLeapYear(y) 46 | let lastDayOfMonth y m d = DateTime.DaysInMonth(y, m) = d 47 | let lastDayOfFebruary (Date(y, m, d) as dt) = m = 2 && lastDayOfMonth y m d 48 | let daysOfMonth y m = DateTime.DaysInMonth(y, m) 49 | let addYears (d: DateTime) n = d.AddYears(n) 50 | 51 | // Find an interval that bounds the root, (shift, factor, maxtTries) are guesses 52 | let findBounds f guess minBound maxBound precision = 53 | if guess <= minBound || guess >= maxBound then throw (sprintf "guess needs to be between %f and %f" minBound maxBound) 54 | let shift = 0.01 55 | let factor = 1.6 56 | let maxTries = 60 57 | let adjValueToMin value = if value <= minBound then minBound + precision else value 58 | let adjValueToMax value = if value >= maxBound then maxBound - precision else value 59 | let rec rfindBounds low up tries = 60 | let tries = tries - 1 61 | if tries = 0 then throw (sprintf "Not found an interval comprising the root after %i tries, last tried was (%f, %f)" maxTries low up) 62 | let lower = adjValueToMin low 63 | let upper = adjValueToMax up 64 | match f lower, f upper with 65 | | x, y when (x*y = 0.) -> lower, upper 66 | | x, y when (x*y < 0.) -> lower, upper 67 | | x, y when (x*y > 0.) -> rfindBounds (lower + factor * (lower - upper)) (upper + factor * (upper - lower)) tries 68 | | x, y -> throw (sprintf "FindBounds: one of the values (%f, %f) cannot be used to evaluate the objective function" lower upper) 69 | let low = adjValueToMin (guess - shift) 70 | let high = adjValueToMax (guess + shift) 71 | rfindBounds low high maxTries 72 | 73 | // Very simple bisection algo. (200) is a guess. It is high. The reason is that if a root doesn't exist, I don't mind the slight perf degradation of 200 iters. But I want to catch it if it exists. 74 | let bisection = 75 | let maxCount = 200 76 | let rec helper f a b count precision = 77 | if a = b then throw (sprintf "(a=b=%f) impossible to start bisection" a) 78 | 79 | let fa = f a 80 | if abs fa < precision then a // a is the root 81 | else 82 | let fb = f b 83 | if abs fb < precision then b // b is the root 84 | else 85 | let newCount = count + 1 86 | 87 | if newCount > maxCount then throw (sprintf "No root found in %i iterations" maxCount) 88 | if fa * fb > 0. then throw (sprintf "(%f,%f) don't bracket the root" a b) 89 | 90 | let midvalue = a + 0.5 * (b - a) 91 | let fmid = f midvalue 92 | 93 | if abs fmid < precision then midvalue // the midvalue is the root 94 | elif fa * fmid < 0. then helper f a midvalue newCount precision 95 | elif fa * fmid > 0. then helper f midvalue b newCount precision 96 | else throw "Bisection: It should never get here" 97 | helper 98 | 99 | let newton = 100 | let maxCount = 20 101 | let rec helper f x count precision = 102 | let d f x = (f (x + precision) - f (x - precision))/(2. * precision) 103 | let fx = f x 104 | let Fx = d f x 105 | let newX = x - (fx / Fx) 106 | if abs (newX - x) < precision then Some( newX ) 107 | elif count > maxCount then None 108 | else helper f newX (count + 1) precision 109 | helper 110 | 111 | // This is my main root finding algo. My strategy is to try a fast but not precise one (newton) first. 112 | // If the result is sensible (it exist and has the same sign as guess), then return it, else try bisection. 113 | // I'm sure more complex way to pick algos exist (i.e. Brent's method). But I favor simplicity here ... 114 | let findRoot f guess = 115 | let precision = 0.000001 // Excel precision on this, from docs 116 | let newtValue = newton f guess 0 precision 117 | if newtValue.IsSome && sign guess = sign newtValue.Value 118 | then newtValue.Value 119 | else 120 | let lower, upper = findBounds f guess -1.0 Double.MaxValue precision 121 | bisection f lower upper 0 precision 122 | 123 | let memoize f = 124 | let m = new Dictionary<_,_> () 125 | fun x -> 126 | lock m (fun () -> 127 | let foundIt, res = m.TryGetValue(x) 128 | if foundIt then res 129 | else 130 | let r = f x 131 | m.Add(x, r) 132 | r 133 | ) -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/excel.fs: -------------------------------------------------------------------------------- 1 | // Wrappers for Excel functions to be used in the testcases 2 | #light 3 | namespace Excel.FinancialFunctions 4 | open Microsoft.Office.Interop.Excel // You need Excel 12 to test this 5 | 6 | open System 7 | open System.Runtime.InteropServices // For COMException 8 | open Excel.FinancialFunctions.Common 9 | open Excel.FinancialFunctions.Tvm 10 | open Excel.FinancialFunctions.DayCount 11 | 12 | module internal ExcelTesting = 13 | // Need to be a singleton for perf reasons 14 | let app = new ApplicationClass() 15 | let funcs = app.WorksheetFunction 16 | 17 | let pvEx r nper pmt fv (pd:PaymentDue) = funcs.Pv(r, nper, pmt, fv, int pd) 18 | let fvEx r nper pmt thePv (pd:PaymentDue) = funcs.Fv(r, nper, pmt, thePv, int pd) 19 | let pmtEx r nper pv fv (pd:PaymentDue) = funcs.Pmt(r, nper, pv, fv, int pd) 20 | let ipmtEx r per nper pv fv (pd:PaymentDue) = funcs.Ipmt(r, per, nper, pv, fv, int pd) 21 | let ppmtEx r per nper pv fv (pd:PaymentDue) = funcs.Ppmt(r, per, nper, pv, fv, int pd) 22 | let nperEx r pmt pv fv (pd:PaymentDue) = float (funcs.NPer(r, pmt, pv, fv, float pd)) 23 | let rateEx nper pmt pv fv (pd:PaymentDue) guess = funcs.Rate (nper, pmt, pv, fv, float pd, guess) 24 | let fvScheduleEx pv interests = funcs.FVSchedule(pv, interests) 25 | let irrEx cfs guess = funcs.Irr(cfs, guess) 26 | let npvEx r cfs = funcs.Npv(r, cfs) 27 | let mirrEx cfs financeRate reinvestRate = funcs.MIrr(cfs, financeRate, reinvestRate) 28 | // Is there a bug in the Excel managed OM in that Worksheet.Xnpv takes 2 params instead of three?? 29 | // Trying to make it work with app.Run, but giving up after a day. XIRR should provide enough test 30 | //let convertDatesToLong (dates:array) = dates |> Array.map (fun x -> x.ToOADate()) 31 | //let xnpvEx r cfs dates: float = Convert.ToDouble (app.Run(Macro = "atpvbaen.xlam!XNPV", Arg1 = r, Arg2 = cfs, Arg3 = (convertDatesToLong dates) )) 32 | let xirrEx cfs dates guess = funcs.Xirr(cfs, dates, guess) 33 | let dbEx cost salvage life period month = funcs.Db(cost, salvage, life, period, month) 34 | let slnEx cost salvage life = funcs.Sln(cost, salvage, life) 35 | let sydEx cost salvage life period = funcs.Syd(cost, salvage, life, period) 36 | let ddbEx cost salvage life period factor = funcs.Ddb(cost, salvage, life, period, factor) 37 | let vdbEx cost salvage life startPeriod endPeriod factor bflag = funcs.Vdb(cost, salvage, life, startPeriod, endPeriod, factor, bflag) 38 | let cumipmtEx r nper pv startPeriod endPeriod (pd:PaymentDue) = funcs.CumIPmt(r, nper, pv, startPeriod, endPeriod, int pd) 39 | let cumprincEx r nper pv startPeriod endPeriod (pd:PaymentDue) = funcs.CumPrinc(r, nper, pv, startPeriod, endPeriod, int pd) 40 | let ispmtEx r per nper pv = funcs.Ispmt (r, per, nper, pv) 41 | let coupDaysEx settl mat (freq:Frequency) (basis:DayCountBasis) = funcs.CoupDays (settl, mat, int freq, int basis) 42 | let coupPCDEx settl mat (freq:Frequency) (basis:DayCountBasis) = float (DateTime.FromOADate (funcs.CoupPcd (settl, mat, int freq, int basis))).Ticks 43 | let coupNCDEx settl mat (freq:Frequency) (basis:DayCountBasis) = float (DateTime.FromOADate (funcs.CoupNcd (settl, mat, int freq, int basis))).Ticks 44 | let coupNumEx settl mat (freq:Frequency) (basis:DayCountBasis) = funcs.CoupNum (settl, mat, int freq, int basis) 45 | let coupDaysBSEx settl mat (freq:Frequency) (basis:DayCountBasis) = funcs.CoupDayBs (settl, mat, int freq, int basis) 46 | let coupDaysNCEx settl mat (freq:Frequency) (basis:DayCountBasis) = funcs.CoupDaysNc (settl, mat, int freq, int basis) 47 | let accrIntMEx issue settlement rate par (basis:DayCountBasis) = funcs.AccrIntM(issue, settlement, rate, par, int basis) 48 | let accrIntEx issue firstInterest settlement rate par (frequency:Frequency) (basis:DayCountBasis) = funcs.AccrInt(issue, firstInterest, settlement, rate, par, int frequency, int basis) 49 | let priceEx settlement maturity rate yld redemption (frequency:Frequency) basis = funcs.Price(settlement, maturity, rate, yld, redemption, int frequency, basis) 50 | let priceMatEx settlement maturity issue rate yld basis = funcs.PriceMat(settlement, maturity, issue, rate, yld, basis) 51 | let yieldMatEx settlement maturity issue rate pr basis = funcs.YieldMat(settlement, maturity, issue, rate, pr, basis) 52 | let yearFracEx startDate endDate basis = funcs.YearFrac(startDate, endDate, basis) 53 | let intRateEx settlement maturity investment redemption basis = funcs.IntRate(settlement, maturity, investment, redemption, basis) 54 | let receivedEx settlement maturity investment discount basis = funcs.Received(settlement, maturity, investment, discount, basis) 55 | let discEx settlement maturity pr redemption basis = funcs.Disc(settlement, maturity, pr, redemption, basis) 56 | let priceDiscEx settlement maturity discount redemption basis = funcs.PriceDisc(settlement, maturity, discount, redemption, basis) 57 | let yieldDiscEx settlement maturity pr redemption basis = funcs.YieldDisc(settlement, maturity, pr, redemption, basis) 58 | let TBillEqEx settlement maturity discount = funcs.TBillEq(settlement, maturity, discount) 59 | let TBillYieldEx settlement maturity pr = funcs.TBillYield(settlement, maturity, pr) 60 | let TBillPriceEx settlement maturity discount = funcs.TBillPrice(settlement, maturity, discount) 61 | let dollarDeEx fractionalDollar fraction = funcs.DollarDe(fractionalDollar, fraction) 62 | let dollarFrEx fractionalDollar fraction = funcs.DollarFr(fractionalDollar, fraction) 63 | let effectEx nominalRate npery = funcs.Effect(nominalRate, npery) 64 | let nominalEx effectRate npery = funcs.Nominal(effectRate, npery) 65 | let durationEx settlement maturity coupon yld frequency basis = funcs.Duration(settlement, maturity, coupon, yld, frequency, basis) 66 | let mdurationEx settlement maturity coupon yld frequency basis = funcs.MDuration(settlement, maturity, coupon, yld, frequency, basis) 67 | let oddFPriceEx settlement maturity issue firstCoupon rate yld redemption (frequency:Frequency) basis = funcs.OddFPrice(settlement, maturity, issue, firstCoupon, rate, yld, redemption, int frequency, basis) 68 | let oddFYieldEx settlement maturity issue firstCoupon rate pr redemption (frequency:Frequency) basis = funcs.OddFYield(settlement, maturity, issue, firstCoupon, rate, pr, redemption, int frequency, basis) 69 | let oddLPriceEx settlement maturity lastInterest rate yld redemption (frequency:Frequency) basis = funcs.OddLPrice(settlement, maturity, lastInterest, rate, yld, redemption, int frequency, basis) 70 | let oddLYieldEx settlement maturity lastInterest rate pr redemption (frequency:Frequency) basis = funcs.OddLYield(settlement, maturity, lastInterest, rate, pr, redemption, int frequency, basis) 71 | let amorLincEx cost datePurchased firstPeriod salvage period rate (basis:DayCountBasis) = funcs.AmorLinc(cost, datePurchased, firstPeriod, salvage, period, rate, basis) 72 | let amorDegrcEx cost datePurchased firstPeriod salvage period rate (basis:DayCountBasis) = funcs.AmorDegrc(cost, datePurchased, firstPeriod, salvage, period, rate, basis) 73 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/testinfrastructure.fs: -------------------------------------------------------------------------------- 1 | // Test infrastructure. The idea is that Excel is the oracle. 2 | // I test a whole bunch of different values for parameters against Excel and check that the result is the same. 3 | #light 4 | namespace Excel.FinancialFunctions 5 | 6 | open System 7 | open System.Collections 8 | open Excel.FinancialFunctions.ExcelTesting 9 | open Excel.FinancialFunctions.Common 10 | open Excel.FinancialFunctions.Tvm 11 | 12 | module internal TestInfrastructure = 13 | 14 | let totTries = ref 0 15 | let totSuccess = ref 0 16 | // Closing down Excel singleton 17 | let endTests () = 18 | printfn "###### Total Test Cases Succeeded %i/%i" !totSuccess !totTries |> ignore 19 | app.Quit() 20 | Console.ReadKey () 21 | 22 | let tuplize f = fun (x, y) -> f x y 23 | 24 | type Result = 25 | | Ok 26 | | Fail of (float * float * obj) 27 | | Exn of (exn * obj) 28 | 29 | let check1 f1 f2 inp = 30 | try 31 | let r1,r2 = f1 inp, f2 inp 32 | if areEqual r1 r2 then 33 | Ok 34 | else 35 | Fail(r1,r2,box(inp)) 36 | with exn -> 37 | Exn (exn,box(inp)) 38 | 39 | let test1 f1 f2 inputs precond = 40 | let tries = ref 0 41 | let successes = ref 0 42 | let failures = new ResizeArray<_>() 43 | let exceptions = new ResizeArray<_>() 44 | for inp in inputs do 45 | if precond inp then 46 | incr tries; 47 | incr totTries 48 | let res = check1 f1 f2 inp 49 | match res with 50 | | Ok -> 51 | incr successes 52 | incr totSuccess 53 | | Fail(data) -> failures.Add(data) 54 | | Exn(data) -> exceptions.Add(data) 55 | !successes, !tries, failures.ToArray(), exceptions.ToArray() 56 | 57 | let square s1 s2 = 58 | seq { for x in s1 do for y in s2 do yield (x,y) } 59 | 60 | /// Pack an pair of sequences together 61 | let pack checker f1 f2 s1 s2 = 62 | checker (fun (x,y) -> f1 x y) (fun (x,y) -> f2 x y) (square s1 s2) 63 | let test2 f1 f2 s1 s2 precond = pack test1 f1 f2 s1 s2 (tuplize precond) 64 | let test3 f1 f2 s1 s2 s3 precond = pack test2 f1 f2 s1 s2 s3 (tuplize precond) 65 | let test4 f1 f2 s1 s2 s3 s4 precond = pack test3 f1 f2 s1 s2 s3 s4 (tuplize precond) 66 | let test5 f1 f2 s1 s2 s3 s4 s5 precond = pack test4 f1 f2 s1 s2 s3 s4 s5 (tuplize precond) 67 | let test6 f1 f2 s1 s2 s3 s4 s5 s6 precond = pack test5 f1 f2 s1 s2 s3 s4 s5 s6 (tuplize precond) 68 | let test7 f1 f2 s1 s2 s3 s4 s5 s6 s7 precond = pack test6 f1 f2 s1 s2 s3 s4 s5 s6 s7 (tuplize precond) 69 | let test8 f1 f2 s1 s2 s3 s4 s5 s6 s7 s8 precond = pack test7 f1 f2 s1 s2 s3 s4 s5 s6 s7 s8 (tuplize precond) 70 | let test9 f1 f2 s1 s2 s3 s4 s5 s6 s7 s8 s9 precond = pack test8 f1 f2 s1 s2 s3 s4 s5 s6 s7 s8 s9 (tuplize precond) 71 | 72 | let precondOk1 _ = true 73 | let precondOk2 _ _ = true 74 | 75 | let spotTest1 f1 f2 p1 = 76 | (areEqual (f1 p1) (f2 p1)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1) (f2 p1)) 77 | let spotTest2 f1 f2 p1 p2 = 78 | (areEqual (f1 p1 p2) (f2 p1 p2)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2) (f2 p1 p2)) 79 | let spotTest3 f1 f2 p1 p2 p3 = 80 | (areEqual (f1 p1 p2 p3) (f2 p1 p2 p3)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3) (f2 p1 p2 p3)) 81 | let spotTest4 f1 f2 p1 p2 p3 p4 = 82 | (areEqual (f1 p1 p2 p3 p4) (f2 p1 p2 p3 p4)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4) (f2 p1 p2 p3 p4)) 83 | let spotTest5 f1 f2 p1 p2 p3 p4 p5 = 84 | (areEqual (f1 p1 p2 p3 p4 p5) (f2 p1 p2 p3 p4 p5)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4 p5) (f2 p1 p2 p3 p4 p5)) 85 | let spotTest6 f1 f2 p1 p2 p3 p4 p5 p6 = 86 | (areEqual (f1 p1 p2 p3 p4 p5 p6) (f2 p1 p2 p3 p4 p5 p6)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4 p5 p6) (f2 p1 p2 p3 p4 p5 p6)) 87 | let spotTest7 f1 f2 p1 p2 p3 p4 p5 p6 p7 = 88 | (areEqual (f1 p1 p2 p3 p4 p5 p6 p7) (f2 p1 p2 p3 p4 p5 p6 p7)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4 p5 p6 p7) (f2 p1 p2 p3 p4 p5 p6 p7)) 89 | let spotTest8 f1 f2 p1 p2 p3 p4 p5 p6 p7 p8 = 90 | (areEqual (f1 p1 p2 p3 p4 p5 p6 p7 p8) (f2 p1 p2 p3 p4 p5 p6 p7 p8)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4 p5 p6 p7 p8) (f2 p1 p2 p3 p4 p5 p6 p7 p8)) 91 | let spotTest9 f1 f2 p1 p2 p3 p4 p5 p6 p7 p8 p9 = 92 | (areEqual (f1 p1 p2 p3 p4 p5 p6 p7 p8 p9) (f2 p1 p2 p3 p4 p5 p6 p7 p8 p9)) |> elseThrow (sprintf "%f <> %f in a spot test" (f1 p1 p2 p3 p4 p5 p6 p7 p8 p9) (f2 p1 p2 p3 p4 p5 p6 p7 p8 p9)) 93 | 94 | // Pretty (?) print functions 95 | let banner name s t = 96 | printfn "## Succeeded %A/%A for %s" s t name 97 | 98 | let printSummary suc tr = printfn "### Successes/tries %i/%i" suc tr 99 | 100 | let printEndSpotTests () = printfn "#### Spot tests completed succesfully as well!!" 101 | 102 | let printExceptions exns = 103 | for e in exns do 104 | let (ex:Exception), parms = e 105 | printfn "\tException < %s > thrown when passing %A" (ex.Message) parms 106 | 107 | let printErrors errors = 108 | for r in errors do 109 | let (r1:float), (r2:float), parms = r 110 | if r1 > 10000000. then 111 | printfn "\t%A <> %A | when passing %A" ((new DateTime(int64 r1)).ToShortDateString()) ((new DateTime(int64 r2)).ToShortDateString()) parms 112 | else 113 | printfn "\t%A <> %A | when passing %A" r1 r2 parms 114 | 115 | let printResults results = 116 | let mutable succ = 0 117 | let mutable tr = 0 118 | for r in results do 119 | let name, (s, t, errors, exns) = r 120 | banner name s t 121 | printErrors errors 122 | printExceptions exns 123 | succ <- succ + s 124 | tr <- tr + t 125 | 126 | 127 | // Multithreading away, adapted from "F# for scientists" ... 128 | open System.Threading 129 | let spawn (f: unit -> unit) = 130 | let thread = new Thread(f) 131 | thread.Start () 132 | thread 133 | 134 | let execute n f = 135 | [|for i in 1 .. n -> 136 | spawn f|] 137 | |> Array.iter (fun t -> t.Join()) 138 | 139 | let next i n () = 140 | if !i = n then None else 141 | incr i 142 | Some(!i - 1) 143 | 144 | let map max_threads a = 145 | let n = Array.length a 146 | let b = Array.create n None 147 | let i = ref 0 148 | let rec apply i = 149 | let (name, func) = a.[i] 150 | b.[i] <- Some(name, func ()) 151 | loop () 152 | and loop () = 153 | Option.iter apply (lock i (next i n)) 154 | execute max_threads loop 155 | Array.map Option.get b 156 | 157 | let cpu_map a = map System.Environment.ProcessorCount a 158 | 159 | -------------------------------------------------------------------------------- /docs/openofficediff.fsx: -------------------------------------------------------------------------------- 1 | (*** hide ***) 2 | #I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0" 3 | #r "ExcelFinancialFunctions.dll" 4 | 5 | open System 6 | open Excel.FinancialFunctions 7 | 8 | (** 9 | Difference between OpenOffice and the library 10 | ============================================= 11 | 12 | The library was designed to be Excel-compliant (see [Compatibility](compatibility.html) section), therefore its behavior is different from OpenOffice/LibreOffice. 13 | Most of the differences are because of the day count conventions and root finding algorithm implementation details. 14 | Some examples are provided below. 15 | *) 16 | 17 | (** 18 | ODDFYIELD, ODDFPRICE 19 | -------------------- 20 | 21 | According to the [OO wiki](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_ODDFYIELD_function), 22 | these functions currently return invalid results (it's #VALUE! even for valid inputs). 23 | *) 24 | 25 | Financial.OddFYield(DateTime(2008, 12, 11), DateTime(2021, 4, 1), DateTime(2008, 10, 15), 26 | DateTime(2009, 4, 1), 0.06, 100., 100., Frequency.Quarterly, DayCountBasis.ActualActual) 27 | // [fsi:Excel: 0.059976999] 28 | Financial.OddFPrice(DateTime(1999, 2, 28), DateTime(2010, 6, 30), DateTime(1998, 2, 28), 29 | DateTime(2009, 6, 30), 0.07, 0.03, 100., Frequency.Annual, DayCountBasis.Actual360) 30 | // [fsi:Excel: 127.9031274] 31 | 32 | (** 33 | ODDLYIELD, OODLPRICE 34 | -------------------- 35 | 36 | The functions return different results. ODDLYIELD example can be found [here](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_ODDLYIELD_function). 37 | *) 38 | 39 | Financial.OddLPrice(DateTime(1999, 2, 28), DateTime(2000, 2, 28), DateTime(1998, 2, 28), 40 | 0.07, 0.03, 130., Frequency.SemiAnnual, DayCountBasis.Actual360) 41 | // [fsi:Excel: 132.8058252 LibreOffice: 132.8407748124] 42 | 43 | Financial.OddLYield(DateTime(1990, 6, 1), DateTime(1995, 12, 31), DateTime(1990, 1, 1), 44 | 0.002, 103., 100., Frequency.Quarterly, DayCountBasis.ActualActual) 45 | // [fsi:Excel: -0.00327563 LibreOffice: -0.002925876] 46 | // Returns the same value even though the frequency is different 47 | Financial.OddLYield(DateTime(1990, 6, 1), DateTime(1995, 12, 31), DateTime(1990, 1, 1), 48 | 0.002, 103., 100., Frequency.Annual, DayCountBasis.ActualActual) 49 | // [fsi:Excel: -0.00327205 LibreOffice: -0.002925876] 50 | 51 | (** 52 | ACCRINT, DISC, DURATION, PRICE, YIELD, INTRATE, TBILL* and others 53 | --------------------------------------------------------- 54 | 55 | Most likely the differences can be explained with YEARFRAC/day count implementations. 56 | DURATION in OO is a completely different function, the analog of Excel one is called DURATION\_ADD. 57 | *) 58 | // in our tests, the numbers for European 30/360 basis were the same. 59 | let accrint basis = 60 | Financial.AccrInt(DateTime(1990, 3, 4), DateTime(1993, 3, 31), 61 | DateTime(1992, 3, 4), 0.07, 10000., Frequency.SemiAnnual, basis) 62 | 63 | accrint DayCountBasis.UsPsa30_360 64 | // [fsi:Excel: 1401.944444 LibreOffice: 1400.000000] 65 | accrint DayCountBasis.ActualActual 66 | // [fsi:Excel: 1398.076923 LibreOffice: 1401.917808] 67 | accrint DayCountBasis.Actual360 68 | // [fsi:Excel: 1394.166667 LibreOffice: 1421.388889] 69 | accrint DayCountBasis.Actual365 70 | // [fsi:Excel: 1399.041096 LibreOffice: 1401.917808] 71 | 72 | Financial.AccrIntM(DateTime(1990, 3, 4), DateTime(2010, 6, 5), 0.1, 12030.34, DayCountBasis.ActualActual) 73 | // [fsi:Excel: 24367.7909 LibreOffice: 24383.68639] 74 | 75 | Financial.Disc(DateTime(2003, 2, 14), DateTime(2004, 3, 31), 23., 100., DayCountBasis.ActualActual) 76 | // [fsi:Excel: 0.684757 LibreOffice: 0.683820] 77 | 78 | Financial.Duration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03, 79 | Frequency.Annual, DayCountBasis.Actual360) 80 | // [fsi:Excel: 8.949173 LibreOffice: 9.254729] 81 | 82 | Financial.MDuration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03, 83 | Frequency.SemiAnnual, DayCountBasis.Actual360) 84 | // [fsi:Excel: 8.860247 LibreOffice: 9.158550] 85 | 86 | Financial.Price(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 0.07, 0.1, 100., 87 | Frequency.Annual, DayCountBasis.Actual360) 88 | // [fsi:Excel: 74.442516 LibreOffice: 74.334983] 89 | 90 | Financial.PriceDisc(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 0.01, 100., DayCountBasis.ActualActual) 91 | // [fsi:Excel: 79.966367 LibreOffice: 80.005464] 92 | 93 | Financial.IntRate(DateTime(1980, 2, 15), DateTime(1980, 5, 4), 23., 130., DayCountBasis.UsPsa30_360) 94 | // [fsi:Excel: 21.199780 LibreOffice: 21.471572] 95 | 96 | Financial.TBillPrice(DateTime(1980, 2, 15), DateTime(1980, 3, 15), 2.) 97 | // [fsi:Excel: 83.888889 LibreOffice: 82.777778] 98 | 99 | Financial.Received(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 200., 0.01, DayCountBasis.ActualActual) 100 | // [fsi:Excel: 250.105148 LibreOffice: 249.982925] 101 | 102 | // the only function which seems to behave differently in Excel Office for Mac 103 | Financial.AmorDegrc(100., DateTime(1998, 2, 28), DateTime(2000, 2, 29), 104 | 10., 0.3, 0.15, DayCountBasis.Actual365, true) 105 | // [fsi:Excel: 0 Excel for Mac: -2 LibreOffice: 75] 106 | 107 | (** 108 | AMORLINC 109 | -------- 110 | 111 | As stated [here](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_AMORLINC_function), 112 | when the date of purchase is the end of a period, Excel regards the initial period 0 as the first full period, 113 | whereas OO regards the initial period as of zero length and returns 0. 114 | However, there're other differences too. 115 | *) 116 | 117 | Financial.AmorLinc(100., DateTime(1998, 2, 28), DateTime(2000, 2, 29), 10., 0., 0.07, DayCountBasis.Actual365) 118 | // [fsi:Excel: 14.000000 LibreOffice: 14.019178] 119 | Financial.AmorLinc(100., DateTime(1998, 2, 28), DateTime(2009, 6, 30), 50., 1.7, 0.1, DayCountBasis.UsPsa30_360) 120 | // [fsi:Excel: 0.0000000 LibreOffice: -63.33333] 121 | 122 | (** 123 | COUPDAYS, COUPDAYSBS, COUPDAYSNC 124 | -------------------------------- 125 | 126 | In Excel the equality `coupDays = coupDaysBS + coupDaysNC` doesn't necessary hold when basis is other than Actual/Actual. 127 | *) 128 | let cdParam = DateTime(1980, 2, 15), DateTime(2000, 2, 28), Frequency.Annual, DayCountBasis.UsPsa30_360 129 | 130 | Financial.CoupDays cdParam 131 | Financial.CoupDaysBS cdParam 132 | Financial.CoupDaysNC cdParam 133 | // [fsi:Excel: 360 <> 345 + 13 LibreOffice: 360 = 345 + 15] 134 | 135 | (** 136 | CUMIPMT, CUMPRINC 137 | ----------------- 138 | 139 | OO analogs are called CUMIPMT\_ADD and CUMPRINC\_ADD (they're expected to be [compatible with Excel](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_CUMIPMT_ADD_function)) 140 | *) 141 | Financial.CumIPmt(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod) 142 | // [fsi:Excel: -59.669577 LibreOffice: -119.669577] 143 | Financial.CumPrinc(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod) 144 | // [fsi:Excel: -0.8811289 LibreOffice: -1.431834] 145 | 146 | (** 147 | DB, DDB 148 | ------- 149 | 150 | Seems like DDB doesn't accept fractional periods. 151 | *) 152 | Financial.Db(100., 10., 1., 0.3, 1.) 153 | // [fsi:Excel: 7.5 LibreOffice: 0.0] 154 | Financial.Ddb(100., 10., 1., 0.3, 1.) 155 | // [fsi:Excel: 90.0 LibreOffice: Err:502] 156 | 157 | (** 158 | IRR, XIRR 159 | --------- 160 | 161 | The implementation details of root finding algorithms might be the cause of differences. 162 | We didn't check all the tests, because OO doesn't accept arrays as parameters (e.g. {-100;100}). But some of them don't work anyway. 163 | *) 164 | Financial.XIrr([206101714.849377; -156650972.54265], [DateTime(2001, 2, 28); DateTime(2001, 3, 31)], -0.1) 165 | // [fsi:Excel: -0.960452 LibreOffice: Err:502 (Invalid argument)] -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/testsdef.fs: -------------------------------------------------------------------------------- 1 | // All the test values that I want to throw at the testcase infrastructure. Just add values here if you want to run 1,000,0000,000 tests 2 | #light 3 | 4 | namespace Excel.FinancialFunctions 5 | 6 | open System 7 | open System.Collections 8 | open Excel.FinancialFunctions.ExcelTesting 9 | open Excel.FinancialFunctions.Common 10 | open Excel.FinancialFunctions.Tvm 11 | open Excel.FinancialFunctions.Irr 12 | open Excel.FinancialFunctions.TestInfrastructure 13 | open Excel.FinancialFunctions.Depreciation 14 | open Excel.FinancialFunctions.DayCount 15 | open Excel.FinancialFunctions.Bonds 16 | open Excel.FinancialFunctions.OddBonds 17 | 18 | module internal TestsDef = 19 | 20 | // Test values 21 | let pvs = [ -300.; -100.; -5.4; 0.; 100.; 150.5] 22 | let fvs = pvs 23 | let rates = [ -1.5; -1.; -2.0; -0.4; -0.1; 0.; 0.6; 1.5] 24 | let npers = [ -2.; 0.; 1.; 2.; 2.7; 10.] 25 | let pers = [ 0.; 1.; 1.3; 2.; 2.5; 5.] 26 | let endPers = [ 1.2; 2.; 3.; 5.; 7.] 27 | let dues = [PaymentDue.BeginningOfPeriod; PaymentDue.EndOfPeriod] 28 | let pmts = [ 50.; 30.; -10.; 0.] 29 | let guesses = [ 0.15; 0.5; 0.] 30 | let pvRates = [0.; -10.; -20.; -100.5] 31 | let fvRates = [0.; 10.; 100.; -20.] 32 | let testInterests = [ [|0.3; -0.5; 0.2; 1.3; -0.2|]; [|0.3; -0.5; 0.2; 0.; -1.2|]] 33 | let testCfs = [[| -100.;10.;10.;100.|]; [| -100.; -10.; 10.; 100.|]; [| -200.; 0.; 10.; -10.; 300.|]] 34 | let testDates = [ 35 | [|date 1970 4 1; date 1972 2 12; date 1980 4 23; date 1983 3 30|]; 36 | [|date 1970 4 1; date 1973 4 12; date 1983 5 23; date 1987 4 30|]; 37 | [|date 1970 4 1; date 1974 2 14; date 1985 6 26; date 1989 5 4|]] 38 | let testCosts = [100.; 200.; -100.] 39 | let testSalvages = [10.;50.; 0.; -20.] 40 | let testLives = [0.; 1.; 13.; 12.7; 40.] 41 | let testPeriods = [0.; 0.3; 1.; 1.7; 2.; 10.; 11.3; 13.] 42 | let testDdbPeriods = [0.3; 1.; 2.; 10.; 11.; 13.] 43 | let testMonths = [1.; 4.; 9.] 44 | let testFactors = [1.;3. ; 4.5; 50.3] 45 | let testEndPeriods = [0.8; 1.; 3.; 4.2; 13.; 3.3; 20.] 46 | let testVdbSwitch = [ VdbSwitch.DontSwitchToStraightLine; VdbSwitch.SwitchToStraightLine] 47 | let testDayCountBasis = [DayCountBasis.Actual360;DayCountBasis.Actual365;DayCountBasis.ActualActual;DayCountBasis.Europ30_360;DayCountBasis.UsPsa30_360] 48 | let testSettlDates = [date 1980 2 15; date 1980 3 15; date 1993 12 31; date 2003 2 14; date 2007 10 31; date 1993 2 28; date 1981 3 31; date 2004 3 31] 49 | let testMatDates = [date 2000 2 28; date 1995 11 30; date 1980 5 4; date 2010 6 30; date 2008 2 29; date 1994 1 31; date 2003 5 14; date 2009 10 1; date 2010 6 5; date 2004 3 31] 50 | let testMatDates2 = [date 2000 2 28; date 1995 11 30; date 1980 5 4; date 2010 6 30; date 2003 5 14; date 2004 3 31] 51 | let testFirstInterestDates = testMatDates2 |> List.map (fun x -> x.AddYears(-1)) |> List.append [date 2000 2 29] 52 | let testSettlDates2 = testFirstInterestDates |> List.map (fun x -> x.AddYears(-1)) 53 | let testIssueDates = testSettlDates2 |> List.map (fun x -> x.AddYears(-1)) 54 | let testFrequency = [Frequency.Annual;Frequency.SemiAnnual;Frequency.Quarterly] 55 | let testIssue = [date 1990 3 4; date 1993 2 28; date 1995 5 31; date 2000 3 28; date 1999 4 2] 56 | let testSettl = [date 1992 3 4; date 1995 3 1; date 1995 2 28; date 1996 3 30; date 2010 6 5; date 2000 7 2] 57 | let testTBillMat1 = testSettlDates |> List.map (fun x -> x.AddDays(+190.)) 58 | let testTBillMat2 = testSettlDates |> List.map (fun x -> x.AddDays(+45.)) 59 | let testTBillMat3 = testSettlDates |> List.map idem 60 | let testTBillMat = testTBillMat1 |> List.append testTBillMat2 |> List.append testTBillMat3 61 | let testBondRates = [0.07; 0.1] 62 | let testDeprRates = [0.15; 0.3; 0.5; 0.1; 0.07] 63 | let testYlds = [ 0.03; 0.1] 64 | let testRedemptions = [100.; 67.; 130.] 65 | let testInvestments = [100.; 23.; 200.] 66 | let testPars = [ 10000.; 12030.34] 67 | let testFirstInt = [date 1990 1 4; date 1993 3 31; date 1988 2 28; date 1986 3 30; date 2010 7 5; date 2002 1 2]; 68 | let testSDates = [date 1980 3 4; date 1993 12 31; date 2003 2 14; date 2007 10 31; date 1993 2 28; date 1981 3 31; date 2000 2 28; 69 | date 1992 1 4; date 1995 3 1; date 1995 2 28; date 1996 3 30; date 2010 6 5; date 2000 1 2; 70 | date 1992 3 4; date 1995 3 1; date 1998 3 30; date 2010 10 5; date 2004 7 2; 71 | date 1990 3 4; date 1993 2 28; date 1995 5 31; date 2000 2 28; date 1999 3 31] 72 | let testEDates = testSDates |> List.map (fun x -> x.AddDays(+1.)) 73 | let testPrices = [ 75. ; 100.; 130.] 74 | let testDiscounts = [ 0.01; 0.25; 0.75; 2.] 75 | let testFractionalDollars = [0.34; 1.02; 2.34; -1.5] 76 | let testFractions = [ 1.; 17.; 20.] 77 | 78 | let testRate () = 79 | spotTest6 calcRate rateEx 1. 10. 100. -100. PaymentDue.EndOfPeriod 0.15 80 | spotTest6 calcRate rateEx 5. 20. 120. -50. PaymentDue.BeginningOfPeriod 0. 81 | spotTest6 calcRate rateEx 10. -10. 0. 100. PaymentDue.EndOfPeriod -0.15 82 | spotTest6 calcRate rateEx 25. -40. -200. 100. PaymentDue.BeginningOfPeriod 0.15 83 | 84 | let testXirrBugs () = 85 | let t = @"-185550.98 5/15/2008 86 | -231887.53 5/19/2008 87 | -26756.74 5/30/2008 88 | -384010.86 6/20/2008 89 | -27114.54 6/26/2008 90 | -458667.97 8/21/2008 91 | -217853.67 9/8/2008 92 | -424924.25 10/13/2008 93 | -75076.01 10/14/2008 94 | -389630.32 10/24/2008 95 | -112094.2 11/19/2008 96 | -25646.4 11/21/2008 97 | -24164.69 11/21/2008 98 | -1222.08 11/21/2008 99 | -556.91 12/3/2008 100 | 1204954.004 12/5/2008" 101 | 102 | let pairs = t.Split([|' '; '\n'; '\r'|]) |> Array.filter (fun x -> not(x = "")) 103 | let dates = pairs |> Array.filter (fun x -> fst(DateTime.TryParse(x))) |> Array.map (fun x -> DateTime.Parse(x)) 104 | let values = pairs |> Array.filter (fun x -> not(fst(DateTime.TryParse(x)))) |> Array.map (fun x -> float x) 105 | let guess = - 0.1 106 | spotTest3 calcXirr xirrEx values dates guess 107 | 108 | let values = [|105091006.;-103250941.864729|] 109 | let dates = [|date 2000 4 10; date 2000 4 30|] 110 | spotTest3 calcXirr xirrEx values dates guess 111 | 112 | let values = [|206101714.849377;-156650972.54265|] 113 | let dates = [|date 2001 2 28; date 2001 3 31|] 114 | spotTest3 calcXirr xirrEx values dates guess 115 | 116 | let values = [|15108163.3840923;-75382259.6628424|] 117 | let dates = [|date 2000 2 29; date 2000 3 31|] 118 | let res = calcXirr values dates guess 119 | areEqual res 165601346.13484925 |> elseThrow "XIrr manual test fails" 120 | 121 | let testOddFYield () = 122 | spotTest9 calcOddFYield oddFYieldEx (date 2008 12 11) (date 2021 4 1) (date 2008 10 15) (date 2009 4 1) 0.06 100. 100. Frequency.Quarterly DayCountBasis.ActualActual 123 | spotTest9 calcOddFYield oddFYieldEx (date 2009 2 28) (date 2020 5 30) (date 2008 9 15) (date 2009 5 30) 0.05 75. 89. Frequency.Annual DayCountBasis.Actual360 124 | spotTest9 calcOddFYield oddFYieldEx (date 2009 10 31) (date 2021 12 31) (date 2009 10 15) (date 2009 12 31) 0.06 100. 100. Frequency.Quarterly DayCountBasis.ActualActual 125 | 126 | let vdbWrap cost salvage life startPeriod endPeriod factor bflag = calcVdb cost salvage life startPeriod endPeriod factor bflag 127 | 128 | let coupPCDWrapper settlement maturity (frequency:Frequency) basis = 129 | float (calcCoupPCD settlement maturity frequency basis).Ticks 130 | 131 | let coupNCDWrapper settlement maturity (frequency:Frequency) basis = 132 | float (calcCoupNCD settlement maturity frequency basis).Ticks 133 | 134 | let calcAccrIntWrap issue firstInterest settlement rate par (frequency:Frequency) basis = 135 | calcAccrInt issue firstInterest settlement rate par frequency basis AccrIntCalcMethod.FromIssueToSettlement 136 | 137 | let amorDegrcWrapper cost datePurchased firstPeriod salvage period rate basis = 138 | calcAmorDegrc cost datePurchased firstPeriod salvage period rate basis true 139 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/crosstests.fs: -------------------------------------------------------------------------------- 1 | #nowarn "25" 2 | 3 | namespace Excel.FinancialFunctions.Tests 4 | 5 | open NUnit.Framework 6 | open System.IO 7 | open System 8 | 9 | [] 10 | module CrossTests = 11 | open Excel.FinancialFunctions 12 | 13 | // I am prototyping a new way of doing cross tests. The idea is to let the NUnit framework 14 | // do more of the work. So we read in the test data into a TestCaseData object, and then 15 | // let NUnit work with that as normal. 16 | // 17 | // Also these files will be slightly different. They'll have a header, because I exported 18 | // them from Excel, and they'll have .csv file extension. 19 | 20 | let readTestCaseData fname goodcases = 21 | Path.Combine(__SOURCE_DIRECTORY__, "testdata", fname + ".csv") 22 | |> File.ReadLines 23 | |> Seq.tail // Skip header 24 | |> Seq.filter (fun line -> not (String.IsNullOrEmpty line)) 25 | |> Seq.filter (fun line -> line.Contains("#NUM!") <> goodcases ) 26 | |> Seq.map (fun line -> line.Split [| ',' |] ) 27 | |> Seq.map TestCaseData 28 | 29 | let inline shouldEqual exp act = 30 | Assert.AreEqual(exp, float act, PRECISION) 31 | 32 | let inline elseThrow s c = if not(c) then failwith s 33 | 34 | let pduration_testdata_fromfile = 35 | readTestCaseData "pduration" true 36 | 37 | [] 38 | let pduration inputs = 39 | let (param,expected) = parse4 inputs 40 | Financial.Pduration param 41 | |> shouldEqual expected 42 | 43 | let pduration_failures_fromfile = 44 | readTestCaseData "pduration" false 45 | 46 | [] 47 | let pduration_fail inputs = 48 | let (param,expected) = parse4 inputs 49 | ( expected = 0.0 ) |> elseThrow "Failure test must not have an expected value" 50 | Assert.Throws(fun () -> Financial.Pduration param |> ignore) |> ignore 51 | 52 | let rri_testdata_fromfile = 53 | readTestCaseData "rri" true 54 | 55 | [] 56 | let rri inputs = 57 | let (param,expected) = parse4 inputs 58 | Financial.Rri param 59 | |> shouldEqual expected 60 | 61 | let rri_failures_fromfile = 62 | readTestCaseData "rri" false 63 | 64 | [] 65 | let rri_fail inputs = 66 | let (param,expected) = parse4 inputs 67 | ( expected = 0.0 ) |> elseThrow "Failure test must not have an expected value" 68 | Assert.Throws(fun () -> Financial.Rri param |> ignore) |> ignore 69 | 70 | // Prices for negative yielding bonds match reverse-figured Excel results to 1e-6 until yields get lower than -0.05% 71 | // Down to -1.0%, the difference is usually pennies. Once the yield reaches -10%, the difference can be up to $150, but let's hope 72 | // we never get there! 73 | // 74 | // See the file yieldnegativefails.csv for the cases which fail 75 | 76 | let yieldnegative_testdata_fromfile = 77 | readTestCaseData "yieldnegative" true 78 | 79 | [] 80 | let yieldnegative inputs = 81 | let (param,expected) = parse8 inputs 82 | Financial.PriceAllowNegativeYield param 83 | |> shouldEqual expected 84 | 85 | [] 86 | let accrint() = runTests "accrint" parse8 Financial.AccrInt 87 | 88 | [] 89 | let accrintm() = runTests "accrintm" parse6 Financial.AccrIntM 90 | 91 | [] 92 | let amordegrc() = runTests "amordegrc" parse9 Financial.AmorDegrc 93 | 94 | [] 95 | let amorlinc() = runTests "amorlinc" parse8 Financial.AmorLinc 96 | 97 | [] 98 | let coupdays() = runTests "coupdays" parse5 Financial.CoupDays 99 | 100 | [] 101 | let coupdaysbs() = runTests "coupdaysbs" parse5 Financial.CoupDaysBS 102 | 103 | [] 104 | let coupdaysnc() = runTests "coupdaysnc" parse5 Financial.CoupDaysNC 105 | 106 | [] 107 | let coupncd() = 108 | // compare DateTime result as ticks to use universal runTests method 109 | runTests "coupncd" parse5 (fun args -> (Financial.CoupNCD args).Ticks) 110 | 111 | [] 112 | let coupnum() = runTests "coupnum" parse5 Financial.CoupNum 113 | 114 | [] 115 | let couppcd() = runTests "couppcd" parse5 (fun args -> (Financial.CoupPCD args).Ticks) 116 | 117 | [] 118 | let cumipm() = runTests "cumipmt" parse7 Financial.CumIPmt 119 | 120 | [] 121 | let cumprinc() = runTests "cumprinc" parse7 Financial.CumPrinc 122 | 123 | [] 124 | let db() = runTests "db" parse6 Financial.Db 125 | 126 | [] 127 | let ddb() = runTests "ddb" parse6 Financial.Ddb 128 | 129 | [] 130 | let disc() = runTests "disc" parse6 Financial.Disc 131 | 132 | [] 133 | let dollarde() = runTests "dollarde" parse3 Financial.DollarDe 134 | 135 | [] 136 | let dollarfr() = runTests "dollarfr" parse3 Financial.DollarFr 137 | 138 | [] 139 | let duration() = runTests "duration" parse7 Financial.Duration 140 | 141 | [] 142 | let effect() = runTests "effect" parse3 Financial.Effect 143 | 144 | [] 145 | let fv() = runTests "fv" parse6 Financial.Fv 146 | 147 | [] 148 | let ipmt() = runTests "ipmt" parse7 Financial.IPmt 149 | 150 | [] 151 | let ispmt() = runTests "ispmt" parse5 Financial.ISPmt 152 | 153 | [] 154 | let intrate() = runTests "intrate" parse6 Financial.IntRate 155 | 156 | [] 157 | let mduration() = runTests "mduration" parse7 Financial.MDuration 158 | 159 | [] 160 | let nper() = runTests "nper" parse6 Financial.NPer 161 | 162 | [] 163 | let nominal() = runTests "nominal" parse3 Financial.Nominal 164 | 165 | [] 166 | let oddfprice() = runTests "oddfprice" parse10 Financial.OddFPrice 167 | 168 | [] 169 | let oddfyield() = runTests "oddfyield" parse10 Financial.OddFYield 170 | 171 | [] 172 | let ppmt() = runTests "ppmt" parse7 Financial.PPmt 173 | 174 | [] 175 | let pmt() = runTests "pmt" parse6 Financial.Pmt 176 | 177 | [] 178 | let price() = runTests "price" parse8 Financial.Price 179 | 180 | [] 181 | let pricedisc() = runTests "pricedisc" parse6 Financial.PriceDisc 182 | 183 | [] 184 | let pricemat() = runTests "pricemat" parse7 Financial.PriceMat 185 | 186 | [] 187 | let pv() = runTests "pv" parse6 Financial.Pv 188 | 189 | [] 190 | let rate() = runTests "rate" parse7 Financial.Rate 191 | 192 | [] 193 | let received() = runTests "received" parse6 Financial.Received 194 | 195 | [] 196 | let sln() = runTests "sln" parse4 Financial.Sln 197 | 198 | [] 199 | let syd() = runTests "syd" parse5 Financial.Syd 200 | 201 | [] 202 | let tbilleq() = runTests "tbilleq" parse4 Financial.TBillEq 203 | 204 | [] 205 | let tbillprice() = runTests "tbillprice" parse4 Financial.TBillPrice 206 | 207 | [] 208 | let tbillyield() = runTests "tbillyield" parse4 Financial.TBillYield 209 | 210 | [] 211 | let vdb() = runTests "vdb" parse8 Financial.Vdb 212 | 213 | [] 214 | let yearfrac() = runTests "yearfrac" parse4 Financial.YearFrac 215 | 216 | [] 217 | let yielddisc() = runTests "yielddisc" parse6 Financial.YieldDisc 218 | 219 | [] 220 | let yieldmat() = runTests "yieldmat" parse7 Financial.YieldMat 221 | 222 | [] 223 | let fvschedule() = runTests "fvschedule" (fun [| pv; interests; res |] -> 224 | (parse pv, parseArray interests), parse res) Financial.FvSchedule 225 | 226 | [] 227 | let irr() = runTests "irr" (fun [| cfs; guess; res |] -> 228 | (parseArray cfs, parse guess), parse res) Financial.Irr 229 | 230 | [] 231 | let npv() = runTests "npv" (fun [| r; cfs; res |] -> 232 | (parse r, parseArray cfs), parse res) Financial.Npv 233 | 234 | [] 235 | let ``npv given irr should be zero``() = 236 | runTests "irr" (fun [| cfs; _; res |] -> 237 | (parse res, parseArray cfs), 0.) Financial.Npv 238 | 239 | [] 240 | let mirr() = runTests "mirr" (fun [| cfs; fr; rr; res |] -> 241 | (parseArray cfs, parse fr, parse rr), parse res) Financial.Mirr 242 | 243 | [] 244 | let xirr() = runTests "xirr" (fun [| cfs; dates; guess; res |] -> 245 | (parseArray cfs, parseDateArray dates, parse guess), parse res) Financial.XIrr 246 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.ConsoleTests/MatrixTests.fs: -------------------------------------------------------------------------------- 1 | namespace Excel.FinancialFunctions 2 | 3 | open NUnit.Framework 4 | open TestInfrastructure 5 | open Excel.FinancialFunctions.ExcelTesting 6 | open Excel.FinancialFunctions.Tvm 7 | open Excel.FinancialFunctions.Loan 8 | open Excel.FinancialFunctions.Irr 9 | open Excel.FinancialFunctions.Depreciation 10 | open Excel.FinancialFunctions.DayCount 11 | open Excel.FinancialFunctions.TestsDef 12 | open Excel.FinancialFunctions.Bonds 13 | open Excel.FinancialFunctions.TBill 14 | open Excel.FinancialFunctions.Misc 15 | open Excel.FinancialFunctions.OddBonds 16 | open Excel.FinancialFunctions.TestPreconditions 17 | 18 | [] 19 | type MatrixTests () = 20 | let tests = [| 21 | // -------------------------------------------------------------------------------------------------- 22 | //"RATE", Excel uses a different root finding algo. Sometimes mine is better, sometimes Excel's is. Using the TestRate function instead. 23 | //"ODDFYIELD", Excel uses a different root finding algo. Sometimes mine is better, sometimes Excel's is. Using the TestOddFYield function instead. 24 | //"XNPV", the excel object model has an xnpv function with a different number of args. Tested through XIRR and separate testcase. 25 | //"YIELD", the excel object model lacks this function. Tested through the price function and separate testcase. 26 | // -------------------------------------------------------------------------------------------------- 27 | "PV", (fun _ -> test5 calcPv pvEx rates npers pmts fvs dues tryPv); 28 | "FV", (fun _ -> test5 calcFv fvEx rates npers pmts pvs dues tryFv); 29 | "PMT", (fun _ -> test5 calcPmt pmtEx rates npers pvs fvs dues tryPmt); 30 | "NPER", (fun _ -> test5 calcNper nperEx rates pmts pvs fvs dues tryNper); 31 | "IPMT", (fun _ -> test6 calcIpmt ipmtEx rates pers npers pvs fvs dues tryIpmt); 32 | "PPMT", (fun _ -> test6 calcPpmt ppmtEx rates pers npers pvs fvs dues tryPpmt); 33 | "CUMIPMT", (fun _ -> test6 calcCumipmt cumipmtEx rates npers pvs pers endPers dues tryCumipmt); 34 | "CUMPRINC", (fun _ -> test6 calcCumprinc cumprincEx rates npers pvs pers endPers dues tryCumprinc); 35 | "ISPMT", (fun _ -> test4 calcIspmt ispmtEx rates pers npers pvs tryIspmt); 36 | "FVSCHEDULE", (fun _ -> test2 calcFvSchedule fvScheduleEx pvs testInterests precondOk2); 37 | "IRR", (fun _ -> test2 calcIrr irrEx testCfs guesses precondOk2); 38 | "NPV", (fun _ -> test2 calcNpv npvEx rates testCfs tryNpv); 39 | "MIRR", (fun _ -> test3 calcMirr mirrEx testCfs rates rates tryMirr); 40 | //"XIRR", fun _-> test3 calcXirr xirrEx testCfs testDates guesses tryXirr; 41 | "DB", fun _ -> test5 calcDb dbEx testCosts testSalvages testLives testPeriods testMonths tryDb; 42 | "SLN", fun _ -> test3 calcSln slnEx testCosts testSalvages testLives trySln; 43 | "SYD", fun _ -> test4 calcSyd sydEx testCosts testSalvages testLives testPeriods trySyd; 44 | "DDB", fun _ -> test5 calcDdb ddbEx testCosts testSalvages testLives testDdbPeriods testFactors tryDdb; 45 | "VDB excluding fractional startdates", fun _ -> test7 vdbWrap vdbEx testCosts testSalvages testLives testPeriods testEndPeriods testFactors testVdbSwitch tryVdb; 46 | "AMORLINC", fun _ -> test7 calcAmorLinc amorLincEx testCosts testIssueDates testFirstInterestDates testSalvages testPeriods testBondRates testDayCountBasis tryAmorLinc; 47 | "AMORDEGRC", fun _ -> test7 amorDegrcWrapper amorDegrcEx testCosts testIssueDates testFirstInterestDates testSalvages testPeriods testDeprRates testDayCountBasis tryAmorDegrc; 48 | "COUPDAYS excluding leap years", fun _ -> test4 calcCoupDays coupDaysEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDays; 49 | "COUPDAYSBS", fun _ -> test4 calcCoupDaysBS coupDaysBSEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDaysBS; 50 | "COUPDAYSNC", fun _ -> test4 calcCoupDaysNC coupDaysNCEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDaysNC; 51 | "COUPNUM", fun _ -> test4 calcCoupNum coupNumEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupNum; 52 | "COUPPCD", fun _ -> test4 coupPCDWrapper coupPCDEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupPCD; 53 | "COUPNCD", fun _ -> test4 coupNCDWrapper coupNCDEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupNCD; 54 | "ACCRINTM", fun _ -> test5 calcAccrIntM accrIntMEx testIssue testSettl testBondRates testPars testDayCountBasis tryAccrIntM; 55 | "ACCRINT", fun _ -> test7 calcAccrIntWrap accrIntEx testIssue testFirstInt testSettl testBondRates testPars testFrequency testDayCountBasis tryAccrInt; 56 | "PRICE", fun _ -> test7 calcPrice priceEx testSettlDates testMatDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryPrice 57 | "PRICEMAT", fun _ -> test6 calcPriceMat priceMatEx testSettlDates testMatDates testIssue testBondRates testYlds testDayCountBasis tryPriceMat 58 | "YIELDMAT", fun _ -> test6 calcYieldMat yieldMatEx testSettlDates testMatDates testIssue testBondRates testPrices testDayCountBasis tryYieldMat 59 | "YEARFRAC", fun _ -> test3 calcYearFrac yearFracEx testSDates testEDates testDayCountBasis tryYearFrac; 60 | "INTRATE", fun _ -> test5 calcIntRate intRateEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryIntRate; 61 | "RECEIVED", fun _ -> test5 calcReceived receivedEx testSettlDates testMatDates testInvestments testDiscounts testDayCountBasis tryReceived; 62 | "DISC", fun _ -> test5 calcDisc discEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryDisc; 63 | "PRICEDISC", fun _ -> test5 calcPriceDisc priceDiscEx testSettlDates testMatDates testDiscounts testRedemptions testDayCountBasis tryPriceDisc; 64 | "YIELDDISC", fun _ -> test5 calcYieldDisc yieldDiscEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryYieldDisc; 65 | "TBILLEQ", fun _ -> test3 calcTBillEq TBillEqEx testSettlDates testTBillMat testDiscounts tryTBillEq; 66 | "TBILLYIELD", fun _ -> test3 calcTBillYield TBillYieldEx testSettlDates testTBillMat testPrices tryTBillYield; 67 | "TBILLPrice", fun _ -> test3 calcTBillPrice TBillPriceEx testSettlDates testTBillMat testDiscounts tryTBillPrice; 68 | "DOLLARDE", fun _ -> test2 calcDollarDe dollarDeEx testFractionalDollars testFractions tryDollarDe; 69 | "DOLLARFR", fun _ -> test2 calcDollarFr dollarFrEx testFractionalDollars testFractions tryDollarFr; 70 | "EFFECT", fun _ -> test2 calcEffect effectEx rates testPeriods tryEffect; 71 | "NOMINAL", fun _ -> test2 calcNominal nominalEx rates testPeriods tryNominal; 72 | "DURATION", fun _ -> test6 calcDuration durationEx testSettlDates testMatDates testInvestments testYlds testFrequency testDayCountBasis tryDuration; 73 | "MDURATION", fun _ -> test6 calcMDuration mdurationEx testSettlDates testMatDates testInvestments testYlds testFrequency testDayCountBasis tryMDuration; 74 | "ODDFPRICE", fun _ -> test9 calcOddFPrice oddFPriceEx testSettlDates2 testMatDates testIssueDates testFirstInterestDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryOddFPrice; 75 | "ODDLPRICE", fun _ -> test8 calcOddLPrice oddLPriceEx testSettlDates2 testMatDates testIssueDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryOddLPrice; 76 | "ODDLYIELD", fun _ -> test8 calcOddLYield oddLYieldEx testSettlDates2 testMatDates testIssueDates testBondRates testRedemptions testRedemptions testFrequency testDayCountBasis tryOddLYield; 77 | |] 78 | 79 | [] 80 | [] 81 | [] 82 | [] 83 | [] 84 | [] 85 | [] 86 | [] 87 | [] 88 | [] 89 | [] 90 | [] 91 | [] 92 | [] 93 | [] 94 | [] 95 | [] 96 | [] 97 | [] 98 | [] 99 | [] 100 | [] 101 | [] 102 | [] 103 | [] 104 | [] 105 | [] 106 | [] 107 | [] 108 | [] 109 | [] 110 | [] 111 | [] 112 | [] 113 | [] 114 | [] 115 | [] 116 | [] 117 | [] 118 | [] 119 | [] 120 | [] 121 | [] 122 | [] 123 | [] 124 | [] 125 | [] 126 | [] 127 | [] 128 | [] 129 | member __.RunMatrix test = 130 | let found = Array.tryFind (fun x -> fst x = test) tests 131 | let (_,func) = found.Value 132 | let (tries,success,_,_) = func () 133 | Assert.AreEqual(success,tries) -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/cumipmt.test: -------------------------------------------------------------------------------- 1 | 0.6,2,100,1,1.2,BeginningOfPeriod,0 2 | 0.6,2,100,1,1.2,EndOfPeriod,-60 3 | 0.6,2,100,1,2,BeginningOfPeriod,-23.07692307692 4 | 0.6,2,100,1,2,EndOfPeriod,-96.92307692308 5 | 0.6,2,100,1.3,2,BeginningOfPeriod,-23.07692307692 6 | 0.6,2,100,1.3,2,EndOfPeriod,-36.92307692308 7 | 0.6,2,100,2,2,BeginningOfPeriod,-23.07692307692 8 | 0.6,2,100,2,2,EndOfPeriod,-36.92307692308 9 | 0.6,2,150.5,1,1.2,BeginningOfPeriod,0 10 | 0.6,2,150.5,1,1.2,EndOfPeriod,-90.3 11 | 0.6,2,150.5,1,2,BeginningOfPeriod,-34.73076923077 12 | 0.6,2,150.5,1,2,EndOfPeriod,-145.8692307692 13 | 0.6,2,150.5,1.3,2,BeginningOfPeriod,-34.73076923077 14 | 0.6,2,150.5,1.3,2,EndOfPeriod,-55.56923076923 15 | 0.6,2,150.5,2,2,BeginningOfPeriod,-34.73076923077 16 | 0.6,2,150.5,2,2,EndOfPeriod,-55.56923076923 17 | 0.6,2.7,100,1,1.2,BeginningOfPeriod,0 18 | 0.6,2.7,100,1,1.2,EndOfPeriod,-60 19 | 0.6,2.7,100,1,2,BeginningOfPeriod,-28.70175552692 20 | 0.6,2.7,100,1,2,EndOfPeriod,-105.9228088431 21 | 0.6,2.7,100,1.3,2,BeginningOfPeriod,-28.70175552692 22 | 0.6,2.7,100,1.3,2,EndOfPeriod,-45.92280884307 23 | 0.6,2.7,100,2,2,BeginningOfPeriod,-28.70175552692 24 | 0.6,2.7,100,2,2,EndOfPeriod,-45.92280884307 25 | 0.6,2.7,150.5,1,1.2,BeginningOfPeriod,0 26 | 0.6,2.7,150.5,1,1.2,EndOfPeriod,-90.3 27 | 0.6,2.7,150.5,1,2,BeginningOfPeriod,-43.19614206801 28 | 0.6,2.7,150.5,1,2,EndOfPeriod,-159.4138273088 29 | 0.6,2.7,150.5,1.3,2,BeginningOfPeriod,-43.19614206801 30 | 0.6,2.7,150.5,1.3,2,EndOfPeriod,-69.11382730882 31 | 0.6,2.7,150.5,2,2,BeginningOfPeriod,-43.19614206801 32 | 0.6,2.7,150.5,2,2,EndOfPeriod,-69.11382730882 33 | 0.6,10,100,1,1.2,BeginningOfPeriod,0 34 | 0.6,10,100,1,1.2,EndOfPeriod,-60 35 | 0.6,10,100,1,2,BeginningOfPeriod,-37.29348545324 36 | 0.6,10,100,1,2,EndOfPeriod,-119.6695767252 37 | 0.6,10,100,1,3,BeginningOfPeriod,-74.25654763166 38 | 0.6,10,100,1,3,EndOfPeriod,-178.8104762107 39 | 0.6,10,100,1,5,BeginningOfPeriod,-146.2794339256 40 | 0.6,10,100,1,5,EndOfPeriod,-294.0470942809 41 | 0.6,10,100,1,7,BeginningOfPeriod,-212.5841471948 42 | 0.6,10,100,1,7,EndOfPeriod,-400.1346355117 43 | 0.6,10,100,1.3,2,BeginningOfPeriod,-37.29348545324 44 | 0.6,10,100,1.3,2,EndOfPeriod,-59.66957672518 45 | 0.6,10,100,1.3,3,BeginningOfPeriod,-74.25654763166 46 | 0.6,10,100,1.3,3,EndOfPeriod,-118.8104762107 47 | 0.6,10,100,1.3,5,BeginningOfPeriod,-146.2794339256 48 | 0.6,10,100,1.3,5,EndOfPeriod,-234.0470942809 49 | 0.6,10,100,1.3,7,BeginningOfPeriod,-212.5841471948 50 | 0.6,10,100,1.3,7,EndOfPeriod,-340.1346355117 51 | 0.6,10,100,2,2,BeginningOfPeriod,-37.29348545324 52 | 0.6,10,100,2,2,EndOfPeriod,-59.66957672518 53 | 0.6,10,100,2,3,BeginningOfPeriod,-74.25654763166 54 | 0.6,10,100,2,3,EndOfPeriod,-118.8104762107 55 | 0.6,10,100,2,5,BeginningOfPeriod,-146.2794339256 56 | 0.6,10,100,2,5,EndOfPeriod,-234.0470942809 57 | 0.6,10,100,2,7,BeginningOfPeriod,-212.5841471948 58 | 0.6,10,100,2,7,EndOfPeriod,-340.1346355117 59 | 0.6,10,100,2.5,3,BeginningOfPeriod,-36.96306217842 60 | 0.6,10,100,2.5,3,EndOfPeriod,-59.14089948548 61 | 0.6,10,100,2.5,5,BeginningOfPeriod,-108.9859484723 62 | 0.6,10,100,2.5,5,EndOfPeriod,-174.3775175557 63 | 0.6,10,100,2.5,7,BeginningOfPeriod,-175.2906617416 64 | 0.6,10,100,2.5,7,EndOfPeriod,-280.4650587865 65 | 0.6,10,100,5,5,BeginningOfPeriod,-35.58850135519 66 | 0.6,10,100,5,5,EndOfPeriod,-56.9416021683 67 | 0.6,10,100,5,7,BeginningOfPeriod,-101.8932146244 68 | 0.6,10,100,5,7,EndOfPeriod,-163.0291433991 69 | 0.6,10,150.5,1,1.2,BeginningOfPeriod,0 70 | 0.6,10,150.5,1,1.2,EndOfPeriod,-90.3 71 | 0.6,10,150.5,1,2,BeginningOfPeriod,-56.12669560713 72 | 0.6,10,150.5,1,2,EndOfPeriod,-180.1027129714 73 | 0.6,10,150.5,1,3,BeginningOfPeriod,-111.7561041857 74 | 0.6,10,150.5,1,3,EndOfPeriod,-269.109766697 75 | 0.6,10,150.5,1,5,BeginningOfPeriod,-220.150548058 76 | 0.6,10,150.5,1,5,EndOfPeriod,-442.5408768928 77 | 0.6,10,150.5,1,7,BeginningOfPeriod,-319.9391415282 78 | 0.6,10,150.5,1,7,EndOfPeriod,-602.2026264451 79 | 0.6,10,150.5,1.3,2,BeginningOfPeriod,-56.12669560713 80 | 0.6,10,150.5,1.3,2,EndOfPeriod,-89.8027129714 81 | 0.6,10,150.5,1.3,3,BeginningOfPeriod,-111.7561041857 82 | 0.6,10,150.5,1.3,3,EndOfPeriod,-178.809766697 83 | 0.6,10,150.5,1.3,5,BeginningOfPeriod,-220.150548058 84 | 0.6,10,150.5,1.3,5,EndOfPeriod,-352.2408768928 85 | 0.6,10,150.5,1.3,7,BeginningOfPeriod,-319.9391415282 86 | 0.6,10,150.5,1.3,7,EndOfPeriod,-511.9026264451 87 | 0.6,10,150.5,2,2,BeginningOfPeriod,-56.12669560713 88 | 0.6,10,150.5,2,2,EndOfPeriod,-89.8027129714 89 | 0.6,10,150.5,2,3,BeginningOfPeriod,-111.7561041857 90 | 0.6,10,150.5,2,3,EndOfPeriod,-178.809766697 91 | 0.6,10,150.5,2,5,BeginningOfPeriod,-220.150548058 92 | 0.6,10,150.5,2,5,EndOfPeriod,-352.2408768928 93 | 0.6,10,150.5,2,7,BeginningOfPeriod,-319.9391415282 94 | 0.6,10,150.5,2,7,EndOfPeriod,-511.9026264451 95 | 0.6,10,150.5,2.5,3,BeginningOfPeriod,-55.62940857853 96 | 0.6,10,150.5,2.5,3,EndOfPeriod,-89.00705372564 97 | 0.6,10,150.5,2.5,5,BeginningOfPeriod,-164.0238524509 98 | 0.6,10,150.5,2.5,5,EndOfPeriod,-262.4381639214 99 | 0.6,10,150.5,2.5,7,BeginningOfPeriod,-263.8124459211 100 | 0.6,10,150.5,2.5,7,EndOfPeriod,-422.0999134737 101 | 0.6,10,150.5,5,5,BeginningOfPeriod,-53.56069453956 102 | 0.6,10,150.5,5,5,EndOfPeriod,-85.69711126329 103 | 0.6,10,150.5,5,7,BeginningOfPeriod,-153.3492880098 104 | 0.6,10,150.5,5,7,EndOfPeriod,-245.3588608156 105 | 1.5,2,100,1,1.2,BeginningOfPeriod,0 106 | 1.5,2,100,1,1.2,EndOfPeriod,-150 107 | 1.5,2,100,1,2,BeginningOfPeriod,-42.85714285714 108 | 1.5,2,100,1,2,EndOfPeriod,-257.1428571429 109 | 1.5,2,100,1.3,2,BeginningOfPeriod,-42.85714285714 110 | 1.5,2,100,1.3,2,EndOfPeriod,-107.1428571429 111 | 1.5,2,100,2,2,BeginningOfPeriod,-42.85714285714 112 | 1.5,2,100,2,2,EndOfPeriod,-107.1428571429 113 | 1.5,2,150.5,1,1.2,BeginningOfPeriod,0 114 | 1.5,2,150.5,1,1.2,EndOfPeriod,-225.75 115 | 1.5,2,150.5,1,2,BeginningOfPeriod,-64.5 116 | 1.5,2,150.5,1,2,EndOfPeriod,-387 117 | 1.5,2,150.5,1.3,2,BeginningOfPeriod,-64.5 118 | 1.5,2,150.5,1.3,2,EndOfPeriod,-161.25 119 | 1.5,2,150.5,2,2,BeginningOfPeriod,-64.5 120 | 1.5,2,150.5,2,2,EndOfPeriod,-161.25 121 | 1.5,2.7,100,1,1.2,BeginningOfPeriod,0 122 | 1.5,2.7,100,1,1.2,EndOfPeriod,-150 123 | 1.5,2.7,100,1,2,BeginningOfPeriod,-51.72006687984 124 | 1.5,2.7,100,1,2,EndOfPeriod,-279.3001671996 125 | 1.5,2.7,100,1.3,2,BeginningOfPeriod,-51.72006687984 126 | 1.5,2.7,100,1.3,2,EndOfPeriod,-129.3001671996 127 | 1.5,2.7,100,2,2,BeginningOfPeriod,-51.72006687984 128 | 1.5,2.7,100,2,2,EndOfPeriod,-129.3001671996 129 | 1.5,2.7,150.5,1,1.2,BeginningOfPeriod,0 130 | 1.5,2.7,150.5,1,1.2,EndOfPeriod,-225.75 131 | 1.5,2.7,150.5,1,2,BeginningOfPeriod,-77.83870065416 132 | 1.5,2.7,150.5,1,2,EndOfPeriod,-420.3467516354 133 | 1.5,2.7,150.5,1.3,2,BeginningOfPeriod,-77.83870065416 134 | 1.5,2.7,150.5,1.3,2,EndOfPeriod,-194.5967516354 135 | 1.5,2.7,150.5,2,2,BeginningOfPeriod,-77.83870065416 136 | 1.5,2.7,150.5,2,2,EndOfPeriod,-194.5967516354 137 | 1.5,10,100,1,1.2,BeginningOfPeriod,0 138 | 1.5,10,100,1,1.2,EndOfPeriod,-150 139 | 1.5,10,100,1,2,BeginningOfPeriod,-59.99056182634 140 | 1.5,10,100,1,2,EndOfPeriod,-299.9764045658 141 | 1.5,10,100,1,3,BeginningOfPeriod,-119.9575282185 142 | 1.5,10,100,1,3,EndOfPeriod,-449.8938205463 143 | 1.5,10,100,1,5,BeginningOfPeriod,-239.6260123686 144 | 1.5,10,100,1,5,EndOfPeriod,-749.0650309214 145 | 1.5,10,100,1,7,BeginningOfPeriod,-357.4879710907 146 | 1.5,10,100,1,7,EndOfPeriod,-1043.719927727 147 | 1.5,10,100,1.3,2,BeginningOfPeriod,-59.99056182634 148 | 1.5,10,100,1.3,2,EndOfPeriod,-149.9764045658 149 | 1.5,10,100,1.3,3,BeginningOfPeriod,-119.9575282185 150 | 1.5,10,100,1.3,3,EndOfPeriod,-299.8938205463 151 | 1.5,10,100,1.3,5,BeginningOfPeriod,-239.6260123686 152 | 1.5,10,100,1.3,5,EndOfPeriod,-599.0650309214 153 | 1.5,10,100,1.3,7,BeginningOfPeriod,-357.4879710907 154 | 1.5,10,100,1.3,7,EndOfPeriod,-893.7199277267 155 | 1.5,10,100,2,2,BeginningOfPeriod,-59.99056182634 156 | 1.5,10,100,2,2,EndOfPeriod,-149.9764045658 157 | 1.5,10,100,2,3,BeginningOfPeriod,-119.9575282185 158 | 1.5,10,100,2,3,EndOfPeriod,-299.8938205463 159 | 1.5,10,100,2,5,BeginningOfPeriod,-239.6260123686 160 | 1.5,10,100,2,5,EndOfPeriod,-599.0650309214 161 | 1.5,10,100,2,7,BeginningOfPeriod,-357.4879710907 162 | 1.5,10,100,2,7,EndOfPeriod,-893.7199277267 163 | 1.5,10,100,2.5,3,BeginningOfPeriod,-59.96696639218 164 | 1.5,10,100,2.5,3,EndOfPeriod,-149.9174159804 165 | 1.5,10,100,2.5,5,BeginningOfPeriod,-179.6354505422 166 | 1.5,10,100,2.5,5,EndOfPeriod,-449.0886263555 167 | 1.5,10,100,2.5,7,BeginningOfPeriod,-297.4974092643 168 | 1.5,10,100,2.5,7,EndOfPeriod,-743.7435231609 169 | 1.5,10,100,5,5,BeginningOfPeriod,-59.76050634327 170 | 1.5,10,100,5,5,EndOfPeriod,-149.4012658582 171 | 1.5,10,100,5,7,BeginningOfPeriod,-177.6224650654 172 | 1.5,10,100,5,7,EndOfPeriod,-444.0561626635 173 | 1.5,10,150.5,1,1.2,BeginningOfPeriod,0 174 | 1.5,10,150.5,1,1.2,EndOfPeriod,-225.75 175 | 1.5,10,150.5,1,2,BeginningOfPeriod,-90.28579554864 176 | 1.5,10,150.5,1,2,EndOfPeriod,-451.4644888716 177 | 1.5,10,150.5,1,3,BeginningOfPeriod,-180.5360799689 178 | 1.5,10,150.5,1,3,EndOfPeriod,-677.0901999221 179 | 1.5,10,150.5,1,5,BeginningOfPeriod,-360.6371486147 180 | 1.5,10,150.5,1,5,EndOfPeriod,-1127.342871537 181 | 1.5,10,150.5,1,7,BeginningOfPeriod,-538.0193964915 182 | 1.5,10,150.5,1,7,EndOfPeriod,-1570.798491229 183 | 1.5,10,150.5,1.3,2,BeginningOfPeriod,-90.28579554864 184 | 1.5,10,150.5,1.3,2,EndOfPeriod,-225.7144888716 185 | 1.5,10,150.5,1.3,3,BeginningOfPeriod,-180.5360799689 186 | 1.5,10,150.5,1.3,3,EndOfPeriod,-451.3401999221 187 | 1.5,10,150.5,1.3,5,BeginningOfPeriod,-360.6371486147 188 | 1.5,10,150.5,1.3,5,EndOfPeriod,-901.5928715367 189 | 1.5,10,150.5,1.3,7,BeginningOfPeriod,-538.0193964915 190 | 1.5,10,150.5,1.3,7,EndOfPeriod,-1345.048491229 191 | 1.5,10,150.5,2,2,BeginningOfPeriod,-90.28579554864 192 | 1.5,10,150.5,2,2,EndOfPeriod,-225.7144888716 193 | 1.5,10,150.5,2,3,BeginningOfPeriod,-180.5360799689 194 | 1.5,10,150.5,2,3,EndOfPeriod,-451.3401999221 195 | 1.5,10,150.5,2,5,BeginningOfPeriod,-360.6371486147 196 | 1.5,10,150.5,2,5,EndOfPeriod,-901.5928715367 197 | 1.5,10,150.5,2,7,BeginningOfPeriod,-538.0193964915 198 | 1.5,10,150.5,2,7,EndOfPeriod,-1345.048491229 199 | 1.5,10,150.5,2.5,3,BeginningOfPeriod,-90.25028442022 200 | 1.5,10,150.5,2.5,3,EndOfPeriod,-225.6257110506 201 | 1.5,10,150.5,2.5,5,BeginningOfPeriod,-270.351353066 202 | 1.5,10,150.5,2.5,5,EndOfPeriod,-675.8783826651 203 | 1.5,10,150.5,2.5,7,BeginningOfPeriod,-447.7336009428 204 | 1.5,10,150.5,2.5,7,EndOfPeriod,-1119.334002357 205 | 1.5,10,150.5,5,5,BeginningOfPeriod,-89.93956204662 206 | 1.5,10,150.5,5,5,EndOfPeriod,-224.8489051166 207 | 1.5,10,150.5,5,7,BeginningOfPeriod,-267.3218099234 208 | 1.5,10,150.5,5,7,EndOfPeriod,-668.3045248085 209 | 0.2,10,100,2,5,EndOfPeriod,-70.59428367948 210 | 0.2,10,100,2,5,EndOfPeriod,-70.59428367948 211 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010-2013 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | 16 | Apache License, Version 2.0 17 | =========================== 18 | 19 | Apache License 20 | Version 2.0, January 2004 21 | http://www.apache.org/licenses/ 22 | 23 | ------------------------------------------------------------ 24 | 25 | ### TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 26 | 27 | 28 | **1. Definitions.** 29 | 30 | - "License" shall mean the terms and conditions for use, reproduction, 31 | and distribution as defined by Sections 1 through 9 of this document. 32 | 33 | - "Licensor" shall mean the copyright owner or entity authorized by 34 | the copyright owner that is granting the License. 35 | 36 | - "Legal Entity" shall mean the union of the acting entity and all 37 | other entities that control, are controlled by, or are under common 38 | control with that entity. For the purposes of this definition, 39 | "control" means (i) the power, direct or indirect, to cause the 40 | direction or management of such entity, whether by contract or 41 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 42 | outstanding shares, or (iii) beneficial ownership of such entity. 43 | 44 | - "You" (or "Your") shall mean an individual or Legal Entity 45 | exercising permissions granted by this License. 46 | 47 | - "Source" form shall mean the preferred form for making modifications, 48 | including but not limited to software source code, documentation 49 | source, and configuration files. 50 | 51 | - "Object" form shall mean any form resulting from mechanical 52 | transformation or translation of a Source form, including but 53 | not limited to compiled object code, generated documentation, 54 | and conversions to other media types. 55 | 56 | - "Work" shall mean the work of authorship, whether in Source or 57 | Object form, made available under the License, as indicated by a 58 | copyright notice that is included in or attached to the work 59 | (an example is provided in the Appendix below). 60 | 61 | - "Derivative Works" shall mean any work, whether in Source or Object 62 | form, that is based on (or derived from) the Work and for which the 63 | editorial revisions, annotations, elaborations, or other modifications 64 | represent, as a whole, an original work of authorship. For the purposes 65 | of this License, Derivative Works shall not include works that remain 66 | separable from, or merely link (or bind by name) to the interfaces of, 67 | the Work and Derivative Works thereof. 68 | 69 | - "Contribution" shall mean any work of authorship, including 70 | the original version of the Work and any modifications or additions 71 | to that Work or Derivative Works thereof, that is intentionally 72 | submitted to Licensor for inclusion in the Work by the copyright owner 73 | or by an individual or Legal Entity authorized to submit on behalf of 74 | the copyright owner. For the purposes of this definition, "submitted" 75 | means any form of electronic, verbal, or written communication sent 76 | to the Licensor or its representatives, including but not limited to 77 | communication on electronic mailing lists, source code control systems, 78 | and issue tracking systems that are managed by, or on behalf of, the 79 | Licensor for the purpose of discussing and improving the Work, but 80 | excluding communication that is conspicuously marked or otherwise 81 | designated in writing by the copyright owner as "Not a Contribution." 82 | 83 | - "Contributor" shall mean Licensor and any individual or Legal Entity 84 | on behalf of whom a Contribution has been received by Licensor and 85 | subsequently incorporated within the Work. 86 | 87 | **2. Grant of Copyright License.** 88 | Subject to the terms and conditions of 89 | this License, each Contributor hereby grants to You a perpetual, 90 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 91 | copyright license to reproduce, prepare Derivative Works of, 92 | publicly display, publicly perform, sublicense, and distribute the 93 | Work and such Derivative Works in Source or Object form. 94 | 95 | **3. Grant of Patent License.** 96 | Subject to the terms and conditions of 97 | this License, each Contributor hereby grants to You a perpetual, 98 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 99 | (except as stated in this section) patent license to make, have made, 100 | use, offer to sell, sell, import, and otherwise transfer the Work, 101 | where such license applies only to those patent claims licensable 102 | by such Contributor that are necessarily infringed by their 103 | Contribution(s) alone or by combination of their Contribution(s) 104 | with the Work to which such Contribution(s) was submitted. If You 105 | institute patent litigation against any entity (including a 106 | cross-claim or counterclaim in a lawsuit) alleging that the Work 107 | or a Contribution incorporated within the Work constitutes direct 108 | or contributory patent infringement, then any patent licenses 109 | granted to You under this License for that Work shall terminate 110 | as of the date such litigation is filed. 111 | 112 | **4. Redistribution.** 113 | You may reproduce and distribute copies of the 114 | Work or Derivative Works thereof in any medium, with or without 115 | modifications, and in Source or Object form, provided that You 116 | meet the following conditions: 117 | 118 | - You must give any other recipients of the Work or 119 | Derivative Works a copy of this License; and 120 | 121 | - You must cause any modified files to carry prominent notices 122 | stating that You changed the files; and 123 | 124 | - You must retain, in the Source form of any Derivative Works 125 | that You distribute, all copyright, patent, trademark, and 126 | attribution notices from the Source form of the Work, 127 | excluding those notices that do not pertain to any part of 128 | the Derivative Works; and 129 | 130 | - If the Work includes a "NOTICE" text file as part of its 131 | distribution, then any Derivative Works that You distribute must 132 | include a readable copy of the attribution notices contained 133 | within such NOTICE file, excluding those notices that do not 134 | pertain to any part of the Derivative Works, in at least one 135 | of the following places: within a NOTICE text file distributed 136 | as part of the Derivative Works; within the Source form or 137 | documentation, if provided along with the Derivative Works; or, 138 | within a display generated by the Derivative Works, if and 139 | wherever such third-party notices normally appear. The contents 140 | of the NOTICE file are for informational purposes only and 141 | do not modify the License. You may add Your own attribution 142 | notices within Derivative Works that You distribute, alongside 143 | or as an addendum to the NOTICE text from the Work, provided 144 | that such additional attribution notices cannot be construed 145 | as modifying the License. 146 | 147 | You may add Your own copyright statement to Your modifications and 148 | may provide additional or different license terms and conditions 149 | for use, reproduction, or distribution of Your modifications, or 150 | for any such Derivative Works as a whole, provided Your use, 151 | reproduction, and distribution of the Work otherwise complies with 152 | the conditions stated in this License. 153 | 154 | **5. Submission of Contributions.** 155 | Unless You explicitly state otherwise, 156 | any Contribution intentionally submitted for inclusion in the Work 157 | by You to the Licensor shall be under the terms and conditions of 158 | this License, without any additional terms or conditions. 159 | Notwithstanding the above, nothing herein shall supersede or modify 160 | the terms of any separate license agreement you may have executed 161 | with Licensor regarding such Contributions. 162 | 163 | **6. Trademarks.** 164 | This License does not grant permission to use the trade 165 | names, trademarks, service marks, or product names of the Licensor, 166 | except as required for reasonable and customary use in describing the 167 | origin of the Work and reproducing the content of the NOTICE file. 168 | 169 | **7. Disclaimer of Warranty.** 170 | Unless required by applicable law or 171 | agreed to in writing, Licensor provides the Work (and each 172 | Contributor provides its Contributions) on an "AS IS" BASIS, 173 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 174 | implied, including, without limitation, any warranties or conditions 175 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 176 | PARTICULAR PURPOSE. You are solely responsible for determining the 177 | appropriateness of using or redistributing the Work and assume any 178 | risks associated with Your exercise of permissions under this License. 179 | 180 | **8. Limitation of Liability.** 181 | In no event and under no legal theory, 182 | whether in tort (including negligence), contract, or otherwise, 183 | unless required by applicable law (such as deliberate and grossly 184 | negligent acts) or agreed to in writing, shall any Contributor be 185 | liable to You for damages, including any direct, indirect, special, 186 | incidental, or consequential damages of any character arising as a 187 | result of this License or out of the use or inability to use the 188 | Work (including but not limited to damages for loss of goodwill, 189 | work stoppage, computer failure or malfunction, or any and all 190 | other commercial damages or losses), even if such Contributor 191 | has been advised of the possibility of such damages. 192 | 193 | **9. Accepting Warranty or Additional Liability.** 194 | While redistributing 195 | the Work or Derivative Works thereof, You may choose to offer, 196 | and charge a fee for, acceptance of support, warranty, indemnity, 197 | or other liability obligations and/or rights consistent with this 198 | License. However, in accepting such obligations, You may act only 199 | on Your own behalf and on Your sole responsibility, not on behalf 200 | of any other Contributor, and only if You agree to indemnify, 201 | defend, and hold each Contributor harmless for any liability 202 | incurred by, or claims asserted against, such Contributor by reason 203 | of your accepting any such warranty or additional liability. 204 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/cumprinc.test: -------------------------------------------------------------------------------- 1 | 0.6,2,100,1,1.2,BeginningOfPeriod,-61.53846153846 2 | 0.6,2,100,1,1.2,EndOfPeriod,-38.46153846154 3 | 0.6,2,100,1,2,BeginningOfPeriod,-100 4 | 0.6,2,100,1,2,EndOfPeriod,-100 5 | 0.6,2,100,1.3,2,BeginningOfPeriod,-38.46153846154 6 | 0.6,2,100,1.3,2,EndOfPeriod,-61.53846153846 7 | 0.6,2,100,2,2,BeginningOfPeriod,-38.46153846154 8 | 0.6,2,100,2,2,EndOfPeriod,-61.53846153846 9 | 0.6,2,150.5,1,1.2,BeginningOfPeriod,-92.61538461538 10 | 0.6,2,150.5,1,1.2,EndOfPeriod,-57.88461538462 11 | 0.6,2,150.5,1,2,BeginningOfPeriod,-150.5 12 | 0.6,2,150.5,1,2,EndOfPeriod,-150.5 13 | 0.6,2,150.5,1.3,2,BeginningOfPeriod,-57.88461538462 14 | 0.6,2,150.5,1.3,2,EndOfPeriod,-92.61538461538 15 | 0.6,2,150.5,2,2,BeginningOfPeriod,-57.88461538462 16 | 0.6,2,150.5,2,2,EndOfPeriod,-92.61538461538 17 | 0.6,2.7,100,1,1.2,BeginningOfPeriod,-52.16374078847 18 | 0.6,2.7,100,1,1.2,EndOfPeriod,-23.46198526155 19 | 0.6,2.7,100,1,2,BeginningOfPeriod,-75.62572605002 20 | 0.6,2.7,100,1,2,EndOfPeriod,-61.00116168003 21 | 0.6,2.7,100,1.3,2,BeginningOfPeriod,-23.46198526155 22 | 0.6,2.7,100,1.3,2,EndOfPeriod,-37.53917641848 23 | 0.6,2.7,100,2,2,BeginningOfPeriod,-23.46198526155 24 | 0.6,2.7,100,2,2,EndOfPeriod,-37.53917641848 25 | 0.6,2.7,150.5,1,1.2,BeginningOfPeriod,-78.50642988664 26 | 0.6,2.7,150.5,1,1.2,EndOfPeriod,-35.31028781863 27 | 0.6,2.7,150.5,1,2,BeginningOfPeriod,-113.8167177053 28 | 0.6,2.7,150.5,1,2,EndOfPeriod,-91.80674832844 29 | 0.6,2.7,150.5,1.3,2,BeginningOfPeriod,-35.31028781863 30 | 0.6,2.7,150.5,1.3,2,EndOfPeriod,-56.49646050981 31 | 0.6,2.7,150.5,2,2,BeginningOfPeriod,-35.31028781863 32 | 0.6,2.7,150.5,2,2,EndOfPeriod,-56.49646050981 33 | 0.6,10,100,1,1.2,BeginningOfPeriod,-37.84419091127 34 | 0.6,10,100,1,1.2,EndOfPeriod,-0.5507054580269 35 | 0.6,10,100,1,2,BeginningOfPeriod,-38.39489636929 36 | 0.6,10,100,1,2,EndOfPeriod,-1.43183419087 37 | 0.6,10,100,1,3,BeginningOfPeriod,-39.27602510214 38 | 0.6,10,100,1,3,EndOfPeriod,-2.841640163419 39 | 0.6,10,100,1,5,BeginningOfPeriod,-42.94152063076 40 | 0.6,10,100,1,5,EndOfPeriod,-8.706433009222 41 | 0.6,10,100,1,7,BeginningOfPeriod,-52.32518918405 42 | 0.6,10,100,1,7,EndOfPeriod,-23.72030269448 43 | 0.6,10,100,1.3,2,BeginningOfPeriod,-0.5507054580269 44 | 0.6,10,100,1.3,2,EndOfPeriod,-0.881128732843 45 | 0.6,10,100,1.3,3,BeginningOfPeriod,-1.43183419087 46 | 0.6,10,100,1.3,3,EndOfPeriod,-2.290934705392 47 | 0.6,10,100,1.3,5,BeginningOfPeriod,-5.097329719497 48 | 0.6,10,100,1.3,5,EndOfPeriod,-8.155727551195 49 | 0.6,10,100,1.3,7,BeginningOfPeriod,-14.48099827278 50 | 0.6,10,100,1.3,7,EndOfPeriod,-23.16959723645 51 | 0.6,10,100,2,2,BeginningOfPeriod,-0.5507054580269 52 | 0.6,10,100,2,2,EndOfPeriod,-0.881128732843 53 | 0.6,10,100,2,3,BeginningOfPeriod,-1.43183419087 54 | 0.6,10,100,2,3,EndOfPeriod,-2.290934705392 55 | 0.6,10,100,2,5,BeginningOfPeriod,-5.097329719497 56 | 0.6,10,100,2,5,EndOfPeriod,-8.155727551195 57 | 0.6,10,100,2,7,BeginningOfPeriod,-14.48099827278 58 | 0.6,10,100,2,7,EndOfPeriod,-23.16959723645 59 | 0.6,10,100,2.5,3,BeginningOfPeriod,-0.881128732843 60 | 0.6,10,100,2.5,3,EndOfPeriod,-1.409805972549 61 | 0.6,10,100,2.5,5,BeginningOfPeriod,-4.54662426147 62 | 0.6,10,100,2.5,5,EndOfPeriod,-7.274598818352 63 | 0.6,10,100,2.5,7,BeginningOfPeriod,-13.93029281476 64 | 0.6,10,100,2.5,7,EndOfPeriod,-22.28846850361 65 | 0.6,10,100,5,5,BeginningOfPeriod,-2.255689556078 66 | 0.6,10,100,5,5,EndOfPeriod,-3.609103289725 67 | 0.6,10,100,5,7,BeginningOfPeriod,-11.63935810936 68 | 0.6,10,100,5,7,EndOfPeriod,-18.62297297498 69 | 0.6,10,150.5,1,1.2,BeginningOfPeriod,-56.95550732146 70 | 0.6,10,150.5,1,1.2,EndOfPeriod,-0.8288117143305 71 | 0.6,10,150.5,1,2,BeginningOfPeriod,-57.78431903579 72 | 0.6,10,150.5,1,2,EndOfPeriod,-2.154910457259 73 | 0.6,10,150.5,1,3,BeginningOfPeriod,-59.11041777872 74 | 0.6,10,150.5,1,3,EndOfPeriod,-4.276668445945 75 | 0.6,10,150.5,1,5,BeginningOfPeriod,-64.6269885493 76 | 0.6,10,150.5,1,5,EndOfPeriod,-13.10318167888 77 | 0.6,10,150.5,1,7,BeginningOfPeriod,-78.74940972199 78 | 0.6,10,150.5,1,7,EndOfPeriod,-35.69905555519 79 | 0.6,10,150.5,1.3,2,BeginningOfPeriod,-0.8288117143305 80 | 0.6,10,150.5,1.3,2,EndOfPeriod,-1.326098742929 81 | 0.6,10,150.5,1.3,3,BeginningOfPeriod,-2.154910457259 82 | 0.6,10,150.5,1.3,3,EndOfPeriod,-3.447856731615 83 | 0.6,10,150.5,1.3,5,BeginningOfPeriod,-7.671481227843 84 | 0.6,10,150.5,1.3,5,EndOfPeriod,-12.27436996455 85 | 0.6,10,150.5,1.3,7,BeginningOfPeriod,-21.79390240054 86 | 0.6,10,150.5,1.3,7,EndOfPeriod,-34.87024384086 87 | 0.6,10,150.5,2,2,BeginningOfPeriod,-0.8288117143305 88 | 0.6,10,150.5,2,2,EndOfPeriod,-1.326098742929 89 | 0.6,10,150.5,2,3,BeginningOfPeriod,-2.154910457259 90 | 0.6,10,150.5,2,3,EndOfPeriod,-3.447856731615 91 | 0.6,10,150.5,2,5,BeginningOfPeriod,-7.671481227843 92 | 0.6,10,150.5,2,5,EndOfPeriod,-12.27436996455 93 | 0.6,10,150.5,2,7,BeginningOfPeriod,-21.79390240054 94 | 0.6,10,150.5,2,7,EndOfPeriod,-34.87024384086 95 | 0.6,10,150.5,2.5,3,BeginningOfPeriod,-1.326098742929 96 | 0.6,10,150.5,2.5,3,EndOfPeriod,-2.121757988686 97 | 0.6,10,150.5,2.5,5,BeginningOfPeriod,-6.842669513512 98 | 0.6,10,150.5,2.5,5,EndOfPeriod,-10.94827122162 99 | 0.6,10,150.5,2.5,7,BeginningOfPeriod,-20.96509068621 100 | 0.6,10,150.5,2.5,7,EndOfPeriod,-33.54414509793 101 | 0.6,10,150.5,5,5,BeginningOfPeriod,-3.394812781898 102 | 0.6,10,150.5,5,5,EndOfPeriod,-5.431700451036 103 | 0.6,10,150.5,5,7,BeginningOfPeriod,-17.51723395459 104 | 0.6,10,150.5,5,7,EndOfPeriod,-28.02757432735 105 | 1.5,2,100,1,1.2,BeginningOfPeriod,-71.42857142857 106 | 1.5,2,100,1,1.2,EndOfPeriod,-28.57142857143 107 | 1.5,2,100,1,2,BeginningOfPeriod,-100 108 | 1.5,2,100,1,2,EndOfPeriod,-100 109 | 1.5,2,100,1.3,2,BeginningOfPeriod,-28.57142857143 110 | 1.5,2,100,1.3,2,EndOfPeriod,-71.42857142857 111 | 1.5,2,100,2,2,BeginningOfPeriod,-28.57142857143 112 | 1.5,2,100,2,2,EndOfPeriod,-71.42857142857 113 | 1.5,2,150.5,1,1.2,BeginningOfPeriod,-107.5 114 | 1.5,2,150.5,1,1.2,EndOfPeriod,-43 115 | 1.5,2,150.5,1,2,BeginningOfPeriod,-150.5 116 | 1.5,2,150.5,1,2,EndOfPeriod,-150.5 117 | 1.5,2,150.5,1.3,2,BeginningOfPeriod,-43 118 | 1.5,2,150.5,1.3,2,EndOfPeriod,-107.5 119 | 1.5,2,150.5,2,2,BeginningOfPeriod,-43 120 | 1.5,2,150.5,2,2,EndOfPeriod,-107.5 121 | 1.5,2.7,100,1,1.2,BeginningOfPeriod,-65.51995541344 122 | 1.5,2.7,100,1,1.2,EndOfPeriod,-13.7998885336 123 | 1.5,2.7,100,1,2,BeginningOfPeriod,-79.31984394705 124 | 1.5,2.7,100,1,2,EndOfPeriod,-48.29960986762 125 | 1.5,2.7,100,1.3,2,BeginningOfPeriod,-13.7998885336 126 | 1.5,2.7,100,1.3,2,EndOfPeriod,-34.49972133401 127 | 1.5,2.7,100,2,2,BeginningOfPeriod,-13.7998885336 128 | 1.5,2.7,100,2,2,EndOfPeriod,-34.49972133401 129 | 1.5,2.7,150.5,1,1.2,BeginningOfPeriod,-98.60753289723 130 | 1.5,2.7,150.5,1,1.2,EndOfPeriod,-20.76883224307 131 | 1.5,2.7,150.5,1,2,BeginningOfPeriod,-119.3763651403 132 | 1.5,2.7,150.5,1,2,EndOfPeriod,-72.69091285076 133 | 1.5,2.7,150.5,1.3,2,BeginningOfPeriod,-20.76883224307 134 | 1.5,2.7,150.5,1.3,2,EndOfPeriod,-51.92208060769 135 | 1.5,2.7,150.5,2,2,BeginningOfPeriod,-20.76883224307 136 | 1.5,2.7,150.5,2,2,EndOfPeriod,-51.92208060769 137 | 1.5,10,100,1,1.2,BeginningOfPeriod,-60.00629211578 138 | 1.5,10,100,1,1.2,EndOfPeriod,-0.0157302894404 139 | 1.5,10,100,1,2,BeginningOfPeriod,-60.02202240522 140 | 1.5,10,100,1,2,EndOfPeriod,-0.05505601304139 141 | 1.5,10,100,1,3,BeginningOfPeriod,-60.06134812882 142 | 1.5,10,100,1,3,EndOfPeriod,-0.1533703220439 143 | 1.5,10,100,1,5,BeginningOfPeriod,-60.40544821033 144 | 1.5,10,100,1,5,EndOfPeriod,-1.013620525816 145 | 1.5,10,100,1,7,BeginningOfPeriod,-62.55607371976 146 | 1.5,10,100,1,7,EndOfPeriod,-6.390184299389 147 | 1.5,10,100,1.3,2,BeginningOfPeriod,-0.0157302894404 148 | 1.5,10,100,1.3,2,EndOfPeriod,-0.039325723601 149 | 1.5,10,100,1.3,3,BeginningOfPeriod,-0.05505601304139 150 | 1.5,10,100,1.3,3,EndOfPeriod,-0.1376400326035 151 | 1.5,10,100,1.3,5,BeginningOfPeriod,-0.3991560945501 152 | 1.5,10,100,1.3,5,EndOfPeriod,-0.9978902363753 153 | 1.5,10,100,1.3,7,BeginningOfPeriod,-2.54978160398 154 | 1.5,10,100,1.3,7,EndOfPeriod,-6.374454009949 155 | 1.5,10,100,2,2,BeginningOfPeriod,-0.0157302894404 156 | 1.5,10,100,2,2,EndOfPeriod,-0.039325723601 157 | 1.5,10,100,2,3,BeginningOfPeriod,-0.05505601304139 158 | 1.5,10,100,2,3,EndOfPeriod,-0.1376400326035 159 | 1.5,10,100,2,5,BeginningOfPeriod,-0.3991560945501 160 | 1.5,10,100,2,5,EndOfPeriod,-0.9978902363753 161 | 1.5,10,100,2,7,BeginningOfPeriod,-2.54978160398 162 | 1.5,10,100,2,7,EndOfPeriod,-6.374454009949 163 | 1.5,10,100,2.5,3,BeginningOfPeriod,-0.039325723601 164 | 1.5,10,100,2.5,3,EndOfPeriod,-0.09831430900249 165 | 1.5,10,100,2.5,5,BeginningOfPeriod,-0.3834258051097 166 | 1.5,10,100,2.5,5,EndOfPeriod,-0.9585645127743 167 | 1.5,10,100,2.5,7,BeginningOfPeriod,-2.534051314539 168 | 1.5,10,100,2.5,7,EndOfPeriod,-6.335128286348 169 | 1.5,10,100,5,5,BeginningOfPeriod,-0.2457857725062 170 | 1.5,10,100,5,5,EndOfPeriod,-0.6144644312655 171 | 1.5,10,100,5,7,BeginningOfPeriod,-2.396411281936 172 | 1.5,10,100,5,7,EndOfPeriod,-5.991028204839 173 | 1.5,10,150.5,1,1.2,BeginningOfPeriod,-90.30946963424 174 | 1.5,10,150.5,1,1.2,EndOfPeriod,-0.0236740856078 175 | 1.5,10,150.5,1,2,BeginningOfPeriod,-90.33314371985 176 | 1.5,10,150.5,1,2,EndOfPeriod,-0.0828592996273 177 | 1.5,10,150.5,1,3,BeginningOfPeriod,-90.39232893387 178 | 1.5,10,150.5,1,3,EndOfPeriod,-0.230822334676 179 | 1.5,10,150.5,1,5,BeginningOfPeriod,-90.91019955654 180 | 1.5,10,150.5,1,5,EndOfPeriod,-1.525498891353 181 | 1.5,10,150.5,1,7,BeginningOfPeriod,-94.14689094823 182 | 1.5,10,150.5,1,7,EndOfPeriod,-9.617227370581 183 | 1.5,10,150.5,1.3,2,BeginningOfPeriod,-0.0236740856078 184 | 1.5,10,150.5,1.3,2,EndOfPeriod,-0.0591852140195 185 | 1.5,10,150.5,1.3,3,BeginningOfPeriod,-0.0828592996273 186 | 1.5,10,150.5,1.3,3,EndOfPeriod,-0.2071482490682 187 | 1.5,10,150.5,1.3,5,BeginningOfPeriod,-0.6007299222979 188 | 1.5,10,150.5,1.3,5,EndOfPeriod,-1.501824805745 189 | 1.5,10,150.5,1.3,7,BeginningOfPeriod,-3.837421313989 190 | 1.5,10,150.5,1.3,7,EndOfPeriod,-9.593553284973 191 | 1.5,10,150.5,2,2,BeginningOfPeriod,-0.0236740856078 192 | 1.5,10,150.5,2,2,EndOfPeriod,-0.0591852140195 193 | 1.5,10,150.5,2,3,BeginningOfPeriod,-0.0828592996273 194 | 1.5,10,150.5,2,3,EndOfPeriod,-0.2071482490682 195 | 1.5,10,150.5,2,5,BeginningOfPeriod,-0.6007299222979 196 | 1.5,10,150.5,2,5,EndOfPeriod,-1.501824805745 197 | 1.5,10,150.5,2,7,BeginningOfPeriod,-3.837421313989 198 | 1.5,10,150.5,2,7,EndOfPeriod,-9.593553284973 199 | 1.5,10,150.5,2.5,3,BeginningOfPeriod,-0.0591852140195 200 | 1.5,10,150.5,2.5,3,EndOfPeriod,-0.1479630350487 201 | 1.5,10,150.5,2.5,5,BeginningOfPeriod,-0.5770558366901 202 | 1.5,10,150.5,2.5,5,EndOfPeriod,-1.442639591725 203 | 1.5,10,150.5,2.5,7,BeginningOfPeriod,-3.813747228381 204 | 1.5,10,150.5,2.5,7,EndOfPeriod,-9.534368070953 205 | 1.5,10,150.5,5,5,BeginningOfPeriod,-0.3699075876219 206 | 1.5,10,150.5,5,5,EndOfPeriod,-0.9247689690547 207 | 1.5,10,150.5,5,7,BeginningOfPeriod,-3.606598979313 208 | 1.5,10,150.5,5,7,EndOfPeriod,-9.016497448283 209 | 0.2,10,100,2,5,EndOfPeriod,-24.81481907366 210 | 0.2,10,100,2,5,EndOfPeriod,-24.81481907366 211 | -------------------------------------------------------------------------------- /src/ExcelFinancialFunctions/oddbonds.fs: -------------------------------------------------------------------------------- 1 | // Difficult to digest formulas for odd bonds calculations. Trust the testcases that I got these ones right. 2 | #light 3 | namespace Excel.FinancialFunctions 4 | 5 | open System 6 | open Excel.FinancialFunctions.Common 7 | open Excel.FinancialFunctions.DayCount 8 | 9 | module internal OddBonds = 10 | 11 | // Main formulas 12 | let coupNumber (Date(my, mm, md) as mat) (Date(sy, sm, sd) as settl) numMonths basis isWholeNumber = 13 | let couponsTemp = if isWholeNumber then 0. else 1. 14 | let endOfMonthTemp = lastDayOfMonth my mm md 15 | let endOfMonth = if not(endOfMonthTemp) && mm <> 2 && md > 28 && md < daysOfMonth my mm then lastDayOfMonth sy sm sd else endOfMonthTemp 16 | let startDate = changeMonth settl 0 basis endOfMonth 17 | let coupons = if settl < startDate then couponsTemp + 1. else couponsTemp 18 | let date = changeMonth startDate numMonths basis endOfMonth 19 | let _, _, result = datesAggregate1 date mat numMonths basis (fun pcd ncd -> 1.) coupons endOfMonth 20 | result 21 | 22 | let daysBetweenNotNeg (dc:IDayCount) startDate endDate = 23 | let result = dc.DaysBetween startDate endDate NumDenumPosition.Numerator 24 | if result > 0. then result else 0. 25 | let daysBetweenNotNegPsaHack startDate endDate = 26 | let result = float (dateDiff360Us startDate endDate Method360Us.ModifyBothDates) 27 | if result > 0. then result else 0. 28 | let daysBetweenNotNegWithHack dc startDate endDate basis = 29 | if basis = DayCountBasis.UsPsa30_360 30 | then daysBetweenNotNegPsaHack startDate endDate 31 | else daysBetweenNotNeg dc startDate endDate 32 | 33 | let oddFPrice settlement (Date(my, mm, md) as maturity) issue firstCoupon rate yld redemption frequency basis = 34 | let dc = dayCount basis 35 | let endMonth = lastDayOfMonth my mm md 36 | let numMonths = freq2months frequency 37 | let numMonthsNeg = - numMonths 38 | let e = dc.CoupDays settlement firstCoupon frequency 39 | let n = dc.CoupNum settlement maturity frequency 40 | let m = frequency 41 | let dfc = daysBetweenNotNeg dc issue firstCoupon 42 | if dfc < e then 43 | let dsc = daysBetweenNotNeg dc settlement firstCoupon 44 | let a = daysBetweenNotNeg dc issue settlement 45 | let x = yld / m + 1. 46 | let y = dsc / e 47 | let p1 = x 48 | let p3 = pow p1 (n - 1. + y) 49 | let term1 = redemption / p3 50 | let term2 = 100. * rate / m * dfc / e / pow p1 y 51 | let term3 = aggrBetween 2 (int n) (fun acc index -> acc + 100. * rate / m / (pow p1 (float index - 1. + y))) 0. 52 | let p2 = rate / m 53 | let term4 = a / e * p2 * 100. 54 | term1 + term2 + term3 - term4 55 | else // dfc >= e 56 | let nc = dc.CoupNum issue firstCoupon frequency 57 | let lateCoupon = ref firstCoupon 58 | let aggrFunction acc index = 59 | let earlyCoupon = changeMonth !lateCoupon numMonthsNeg basis false 60 | let nl = 61 | if basis = DayCountBasis.ActualActual 62 | then daysBetweenNotNeg dc earlyCoupon !lateCoupon 63 | else e 64 | let dci = if index > 1 then nl else daysBetweenNotNeg dc issue !lateCoupon 65 | let startDate = if issue > earlyCoupon then issue else earlyCoupon 66 | let endDate = if settlement < !lateCoupon then settlement else !lateCoupon 67 | let a = daysBetweenNotNeg dc startDate endDate 68 | lateCoupon := earlyCoupon 69 | let dcnl, anl = acc 70 | dcnl + dci / nl, anl + a / nl 71 | let dcnl, anl = aggrBetween (int nc) 1 aggrFunction (0., 0.) 72 | let dsc = 73 | if basis = DayCountBasis.Actual360 || basis = DayCountBasis.Actual365 74 | then 75 | let date = dc.CoupNCD settlement firstCoupon frequency 76 | daysBetweenNotNeg dc settlement date 77 | else 78 | let date = dc.CoupPCD settlement firstCoupon frequency 79 | let a = dc.DaysBetween date settlement NumDenumPosition.Numerator 80 | e - a 81 | let nq = coupNumber firstCoupon settlement numMonths basis true 82 | let n = dc.CoupNum firstCoupon maturity frequency 83 | let x = yld / m + 1. 84 | let y = dsc / e 85 | let p1 = x 86 | let p3 = pow p1 (y + nq + n) 87 | let term1 = redemption / p3 88 | let term2 = 100. * rate / m * dcnl / pow p1 (nq + y) 89 | let term3 = aggrBetween 1 (int n) (fun acc index -> acc + 100. * rate / m / (pow p1 (float index + nq + y))) 0. 90 | let term4 = 100. * rate / m * anl 91 | term1 + term2 + term3 - term4 92 | let oddFYield settlement maturity issue firstCoupon rate pr redemption frequency basis = 93 | let dc = dayCount basis 94 | let years = dc.DaysBetween settlement maturity NumDenumPosition.Numerator 95 | let m = frequency 96 | let px = pr - 100. 97 | let num = rate * years * 100. - px 98 | let denum = px / 4. + years * px / 2. + years * 100. 99 | let guess = num / denum 100 | findRoot (fun yld -> pr - oddFPrice settlement maturity issue firstCoupon rate yld redemption frequency basis) guess 101 | let oddLFunc settlement maturity lastInterest rate prOrYld redemption frequency basis isLPrice = 102 | let dc = dayCount basis 103 | let m = frequency 104 | let numMonths = int (12. / frequency) 105 | let lastCoupon = lastInterest 106 | let nc = dc.CoupNum lastCoupon maturity frequency 107 | let earlyCoupon = ref lastCoupon 108 | let aggrFunction acc index = 109 | let lateCoupon = changeMonth !earlyCoupon numMonths basis false 110 | let nl = daysBetweenNotNegWithHack dc !earlyCoupon lateCoupon basis 111 | let dci = if index < int nc then nl else daysBetweenNotNegWithHack dc !earlyCoupon maturity basis 112 | let a = 113 | if lateCoupon < settlement 114 | then dci 115 | elif !earlyCoupon < settlement 116 | then daysBetweenNotNeg dc !earlyCoupon settlement 117 | else 0. 118 | let startDate = if settlement > !earlyCoupon then settlement else !earlyCoupon 119 | let endDate = if maturity < lateCoupon then maturity else lateCoupon 120 | let dsc = daysBetweenNotNeg dc startDate endDate 121 | earlyCoupon := lateCoupon 122 | let dcnl, anl, dscnl = acc 123 | dcnl + dci / nl, anl + a / nl , dscnl + dsc / nl 124 | let dcnl, anl, dscnl = aggrBetween 1 (int nc) aggrFunction (0., 0., 0.) 125 | let x = 100. * rate / m 126 | let term1 = dcnl * x + redemption 127 | if isLPrice then 128 | let term2 = dscnl * prOrYld / m + 1. 129 | let term3 = anl * x 130 | term1 / term2 - term3 131 | else 132 | let term2 = anl * x + prOrYld 133 | let term3 = m / dscnl 134 | (term1 - term2) / term2 * term3 135 | 136 | // Preconditions and special cases 137 | let calcOddFPrice settlement (Date(my, mm, md) as maturity) issue firstCoupon rate yld redemption (frequency:Frequency) basis = 138 | let endMonth = lastDayOfMonth my mm md 139 | let numMonths = int (12. / (float(int frequency))) 140 | let numMonthsNeg = - numMonths 141 | let pcd, ncd = findPcdNcd (changeMonth maturity numMonthsNeg basis endMonth) firstCoupon numMonthsNeg basis endMonth 142 | // The next condition is not in the docs, but nevertheless is needed !!!!! 143 | (pcd = firstCoupon) |> elseThrow "maturity and firstCoupon must have the same month and day (except for February when leap years are considered)" 144 | (maturity > firstCoupon) |> elseThrow "maturity must be after firstCoupon" 145 | (firstCoupon > settlement) |> elseThrow "firstCoupon must be after settlement" 146 | (settlement > issue) |> elseThrow "settlement must be after issue" 147 | (rate >= 0.) |> elseThrow "rate must be more than 0" 148 | (yld >= 0.) |> elseThrow "yld must be more than 0" 149 | (redemption >= 0.) |> elseThrow "redemption must be more than 0" 150 | oddFPrice settlement maturity issue firstCoupon rate yld redemption (float (int frequency)) basis 151 | let calcOddFYield settlement maturity issue firstCoupon rate pr redemption (frequency:Frequency) basis = 152 | (maturity > firstCoupon) |> elseThrow "maturity must be after firstCoupon" 153 | (firstCoupon > settlement) |> elseThrow "firstCoupon must be after settlement" 154 | (settlement > issue) |> elseThrow "settlement must be after issue" 155 | (rate >= 0.) |> elseThrow "rate must be more than 0" 156 | (pr >= 0.) |> elseThrow "pr must be more than 0" 157 | (redemption >= 0.) |> elseThrow "redemption must be more than 0" 158 | oddFYield settlement maturity issue firstCoupon rate pr redemption (float (int frequency)) basis 159 | let calcOddLPrice settlement maturity lastInterest rate yld redemption (frequency:Frequency) basis = 160 | (maturity > settlement) |> elseThrow "maturity must be after settlement" 161 | (settlement > lastInterest) |> elseThrow "settlement must be after lastInterest" 162 | (rate >= 0.) |> elseThrow "rate must be more than 0" 163 | (yld >= 0.) |> elseThrow "yld must be more than 0" 164 | (redemption >= 0.) |> elseThrow "redemption must be more than 0" 165 | oddLFunc settlement maturity lastInterest rate yld redemption (float (int frequency)) basis true 166 | let calcOddLYield settlement maturity lastInterest rate pr redemption (frequency:Frequency) basis = 167 | (maturity > settlement) |> elseThrow "maturity must be after settlement" 168 | (settlement > lastInterest) |> elseThrow "settlement must be after lastInterest" 169 | (rate >= 0.) |> elseThrow "rate must be more than 0" 170 | (pr >= 0.) |> elseThrow "pr must be more than 0" 171 | (redemption >= 0.) |> elseThrow "redemption must be more than 0" 172 | oddLFunc settlement maturity lastInterest rate pr redemption (float (int frequency)) basis false 173 | 174 | -------------------------------------------------------------------------------- /tests/ExcelFinancialFunctions.Tests/testdata/db.test: -------------------------------------------------------------------------------- 1 | 100,10,1,0.3,1,7.5 2 | 100,10,1,0.3,4,30 3 | 100,10,1,0.3,9,67.5 4 | 100,10,1,1,1,7.5 5 | 100,10,1,1,4,30 6 | 100,10,1,1,9,67.5 7 | 100,10,13,0.3,1,1.35 8 | 100,10,13,0.3,4,5.4 9 | 100,10,13,0.3,9,12.15 10 | 100,10,13,1,1,1.35 11 | 100,10,13,1,4,5.4 12 | 100,10,13,1,9,12.15 13 | 100,10,13,1.7,1,1.35 14 | 100,10,13,1.7,4,5.4 15 | 100,10,13,1.7,9,12.15 16 | 100,10,13,2,1,15.9813 17 | 100,10,13,2,4,15.3252 18 | 100,10,13,2,9,14.2317 19 | 100,10,13,10,1,3.88654994585 20 | 100,10,13,10,4,3.726990622174 21 | 100,10,13,10,9,3.461058416046 22 | 100,10,13,11.3,1,3.256928854622 23 | 100,10,13,11.3,4,3.123218141382 24 | 100,10,13,11.3,9,2.900366952647 25 | 100,10,13,13,1,2.287158746586 26 | 100,10,13,13,4,2.193261200476 27 | 100,10,13,13,9,2.036765290294 28 | 100,10,12.7,0.3,1,1.383333333333 29 | 100,10,12.7,0.3,4,5.533333333333 30 | 100,10,12.7,0.3,9,12.45 31 | 100,10,12.7,1,1,1.383333333333 32 | 100,10,12.7,1,4,5.533333333333 33 | 100,10,12.7,1,9,12.45 34 | 100,10,12.7,1.7,1,1.383333333333 35 | 100,10,12.7,1.7,4,5.533333333333 36 | 100,10,12.7,1.7,9,12.45 37 | 100,10,12.7,2,1,16.37036666667 38 | 100,10,12.7,2,4,15.68146666667 39 | 100,10,12.7,2,9,14.5333 40 | 100,10,12.7,10,1,3.831658648126 41 | 100,10,12.7,10,4,3.670414266956 42 | 100,10,12.7,10,9,3.401673631673 43 | 100,10,12.7,11.3,1,3.195603312537 44 | 100,10,12.7,11.3,4,3.061125498642 45 | 100,10,12.7,11.3,9,2.836995808815 46 | 100,10,40,0.3,1,0.4666666666667 47 | 100,10,40,0.3,4,1.866666666667 48 | 100,10,40,0.3,9,4.2 49 | 100,10,40,1,1,0.4666666666667 50 | 100,10,40,1,4,1.866666666667 51 | 100,10,40,1,9,4.2 52 | 100,10,40,1.7,1,0.4666666666667 53 | 100,10,40,1.7,4,1.866666666667 54 | 100,10,40,1.7,9,4.2 55 | 100,10,40,2,1,5.573866666667 56 | 100,10,40,2,4,5.495466666667 57 | 100,10,40,2,9,5.3648 58 | 100,10,40,10,1,3.515058280007 59 | 100,10,40,10,4,3.465616736885 60 | 100,10,40,10,9,3.383214165016 61 | 100,10,40,11.3,1,3.318215016326 62 | 100,10,40,11.3,4,3.27154219962 63 | 100,10,40,11.3,9,3.193754171776 64 | 100,10,40,13,1,2.956980856789 65 | 100,10,40,13,4,2.9153890296 66 | 100,10,40,13,9,2.846069317619 67 | 100,50,1,0.3,1,4.166666666667 68 | 100,50,1,0.3,4,16.66666666667 69 | 100,50,1,0.3,9,37.5 70 | 100,50,1,1,1,4.166666666667 71 | 100,50,1,1,4,16.66666666667 72 | 100,50,1,1,9,37.5 73 | 100,50,13,0.3,1,0.4333333333333 74 | 100,50,13,0.3,4,1.733333333333 75 | 100,50,13,0.3,9,3.9 76 | 100,50,13,1,1,0.4333333333333 77 | 100,50,13,1,4,1.733333333333 78 | 100,50,13,1,9,3.9 79 | 100,50,13,1.7,1,0.4333333333333 80 | 100,50,13,1.7,4,1.733333333333 81 | 100,50,13,1.7,9,3.9 82 | 100,50,13,2,1,5.177466666667 83 | 100,50,13,2,4,5.109866666667 84 | 100,50,13,2,9,4.9972 85 | 100,50,13,10,1,3.37741175209 86 | 100,50,13,10,4,3.333314310399 87 | 100,50,13,10,9,3.259818574247 88 | 100,50,13,11.3,1,3.201786340982 89 | 100,50,13,11.3,4,3.159981966259 90 | 100,50,13,11.3,9,3.090308008386 91 | 100,50,13,13,1,2.877458191786 92 | 100,50,13,13,4,2.839888433004 93 | 100,50,13,13,9,2.777272168369 94 | 100,50,12.7,0.3,1,0.4416666666667 95 | 100,50,12.7,0.3,4,1.766666666667 96 | 100,50,12.7,0.3,9,3.975 97 | 100,50,12.7,1,1,0.4416666666667 98 | 100,50,12.7,1,4,1.766666666667 99 | 100,50,12.7,1,9,3.975 100 | 100,50,12.7,1.7,1,0.4416666666667 101 | 100,50,12.7,1.7,4,1.766666666667 102 | 100,50,12.7,1.7,9,3.975 103 | 100,50,12.7,2,1,5.276591666667 104 | 100,50,12.7,2,4,5.206366666667 105 | 100,50,12.7,2,9,5.089325 106 | 100,50,12.7,10,1,3.413133844485 107 | 100,50,12.7,10,4,3.367709195512 108 | 100,50,12.7,10,9,3.292001447225 109 | 100,50,12.7,11.3,1,3.232237750727 110 | 100,50,12.7,11.3,4,3.18922060815 111 | 100,50,12.7,11.3,9,3.117525370522 112 | 100,50,40,0.3,1,0.1416666666667 113 | 100,50,40,0.3,4,0.5666666666667 114 | 100,50,40,0.3,9,1.275 115 | 100,50,40,1,1,0.1416666666667 116 | 100,50,40,1,4,0.5666666666667 117 | 100,50,40,1,9,1.275 118 | 100,50,40,1.7,1,0.1416666666667 119 | 100,50,40,1.7,4,0.5666666666667 120 | 100,50,40,1.7,9,1.275 121 | 100,50,40,2,1,1.697591666667 122 | 100,50,40,2,4,1.690366666667 123 | 100,50,40,2,9,1.678325 124 | 100,50,40,10,1,1.479998847848 125 | 100,50,40,10,4,1.473699929277 126 | 100,50,40,10,9,1.463201731658 127 | 100,50,40,11.3,1,1.454838867435 128 | 100,50,40,11.3,4,1.448647030479 129 | 100,50,40,11.3,9,1.43832730222 130 | 100,50,40,13,1,1.405794794375 131 | 100,50,40,13,4,1.399811690435 132 | 100,50,40,13,9,1.389839850535 133 | 100,0,1,0.3,1,8.333333333333 134 | 100,0,1,0.3,4,33.33333333333 135 | 100,0,1,0.3,9,75 136 | 100,0,1,1,1,8.333333333333 137 | 100,0,1,1,4,33.33333333333 138 | 100,0,1,1,9,75 139 | 100,0,13,0.3,1,8.333333333333 140 | 100,0,13,0.3,4,33.33333333333 141 | 100,0,13,0.3,9,75 142 | 100,0,13,1,1,8.333333333333 143 | 100,0,13,1,4,33.33333333333 144 | 100,0,13,1,9,75 145 | 100,0,13,1.7,1,8.333333333333 146 | 100,0,13,1.7,4,33.33333333333 147 | 100,0,13,1.7,9,75 148 | 100,0,13,2,1,91.66666666667 149 | 100,0,13,2,4,66.66666666667 150 | 100,0,13,2,9,25 151 | 100,0,13,10,1,0 152 | 100,0,13,10,4,0 153 | 100,0,13,10,9,0 154 | 100,0,13,11.3,1,0 155 | 100,0,13,11.3,4,0 156 | 100,0,13,11.3,9,0 157 | 100,0,13,13,1,0 158 | 100,0,13,13,4,0 159 | 100,0,13,13,9,0 160 | 100,0,12.7,0.3,1,8.333333333333 161 | 100,0,12.7,0.3,4,33.33333333333 162 | 100,0,12.7,0.3,9,75 163 | 100,0,12.7,1,1,8.333333333333 164 | 100,0,12.7,1,4,33.33333333333 165 | 100,0,12.7,1,9,75 166 | 100,0,12.7,1.7,1,8.333333333333 167 | 100,0,12.7,1.7,4,33.33333333333 168 | 100,0,12.7,1.7,9,75 169 | 100,0,12.7,2,1,91.66666666667 170 | 100,0,12.7,2,4,66.66666666667 171 | 100,0,12.7,2,9,25 172 | 100,0,12.7,10,1,0 173 | 100,0,12.7,10,4,0 174 | 100,0,12.7,10,9,0 175 | 100,0,12.7,11.3,1,0 176 | 100,0,12.7,11.3,4,0 177 | 100,0,12.7,11.3,9,0 178 | 100,0,40,0.3,1,8.333333333333 179 | 100,0,40,0.3,4,33.33333333333 180 | 100,0,40,0.3,9,75 181 | 100,0,40,1,1,8.333333333333 182 | 100,0,40,1,4,33.33333333333 183 | 100,0,40,1,9,75 184 | 100,0,40,1.7,1,8.333333333333 185 | 100,0,40,1.7,4,33.33333333333 186 | 100,0,40,1.7,9,75 187 | 100,0,40,2,1,91.66666666667 188 | 100,0,40,2,4,66.66666666667 189 | 100,0,40,2,9,25 190 | 100,0,40,10,1,0 191 | 100,0,40,10,4,0 192 | 100,0,40,10,9,0 193 | 100,0,40,11.3,1,0 194 | 100,0,40,11.3,4,0 195 | 100,0,40,11.3,9,0 196 | 100,0,40,13,1,0 197 | 100,0,40,13,4,0 198 | 100,0,40,13,9,0 199 | 200,10,1,0.3,1,15.83333333333 200 | 200,10,1,0.3,4,63.33333333333 201 | 200,10,1,0.3,9,142.5 202 | 200,10,1,1,1,15.83333333333 203 | 200,10,1,1,4,63.33333333333 204 | 200,10,1,1,9,142.5 205 | 200,10,13,0.3,1,3.433333333333 206 | 200,10,13,0.3,4,13.73333333333 207 | 200,10,13,0.3,9,30.9 208 | 200,10,13,1,1,3.433333333333 209 | 200,10,13,1,4,13.73333333333 210 | 200,10,13,1,9,30.9 211 | 200,10,13,1.7,1,3.433333333333 212 | 200,10,13,1.7,4,13.73333333333 213 | 200,10,13,1.7,9,30.9 214 | 200,10,13,2,1,40.49273333333 215 | 200,10,13,2,4,38.37093333333 216 | 200,10,13,2,9,34.8346 217 | 200,10,13,10,1,6.396480979977 218 | 200,10,13,10,4,6.061308413788 219 | 200,10,13,10,9,5.502687470141 220 | 200,10,13,11.3,1,5.078805898102 221 | 200,10,13,11.3,4,4.812678880548 222 | 200,10,13,11.3,9,4.369133851292 223 | 200,10,13,13,1,3.201862075176 224 | 200,10,13,13,4,3.034086022737 225 | 200,10,13,13,9,2.754459268673 226 | 200,10,12.7,0.3,1,3.5 227 | 200,10,12.7,0.3,4,14 228 | 200,10,12.7,0.3,9,31.5 229 | 200,10,12.7,1,1,3.5 230 | 200,10,12.7,1,4,14 231 | 200,10,12.7,1,9,31.5 232 | 200,10,12.7,1.7,1,3.5 233 | 200,10,12.7,1.7,4,14 234 | 200,10,12.7,1.7,9,31.5 235 | 200,10,12.7,2,1,41.265 236 | 200,10,12.7,2,4,39.06 237 | 200,10,12.7,2,9,35.385 238 | 200,10,12.7,10,1,6.260349504079 239 | 200,10,12.7,10,4,5.925827011495 240 | 200,10,12.7,10,9,5.368289523854 241 | 200,10,12.7,11.3,1,4.945676108223 242 | 200,10,12.7,11.3,4,4.681403339081 243 | 200,10,12.7,11.3,9,4.240948723845 244 | 200,10,40,0.3,1,1.2 245 | 200,10,40,0.3,4,4.8 246 | 200,10,40,0.3,9,10.8 247 | 200,10,40,1,1,1.2 248 | 200,10,40,1,4,4.8 249 | 200,10,40,1,9,10.8 250 | 200,10,40,1.7,1,1.2 251 | 200,10,40,1.7,4,4.8 252 | 200,10,40,1.7,9,10.8 253 | 200,10,40,2,1,14.3136 254 | 200,10,40,2,4,14.0544 255 | 200,10,40,2,9,13.6224 256 | 200,10,40,10,1,7.872862857365 257 | 200,10,40,10,4,7.730295924334 258 | 200,10,40,10,9,7.492684369283 259 | 200,10,40,11.3,1,7.306016731635 260 | 200,10,40,11.3,4,7.173714617782 261 | 200,10,40,11.3,9,6.953211094695 262 | 200,10,40,13,1,6.291824713016 263 | 200,10,40,13,4,6.1778882494 264 | 200,10,40,13,9,5.987994143374 265 | 200,50,1,0.3,1,12.5 266 | 200,50,1,0.3,4,50 267 | 200,50,1,0.3,9,112.5 268 | 200,50,1,1,1,12.5 269 | 200,50,1,1,4,50 270 | 200,50,1,1,9,112.5 271 | 200,50,13,0.3,1,1.683333333333 272 | 200,50,13,0.3,4,6.733333333333 273 | 200,50,13,0.3,9,15.15 274 | 200,50,13,1,1,1.683333333333 275 | 200,50,13,1,4,6.733333333333 276 | 200,50,13,1,9,15.15 277 | 200,50,13,1.7,1,1.683333333333 278 | 200,50,13,1.7,4,6.733333333333 279 | 200,50,13,1.7,9,15.15 280 | 200,50,13,2,1,20.02998333333 281 | 200,50,13,2,4,19.51993333333 282 | 200,50,13,2,9,18.66985 283 | 200,50,13,10,1,8.545906202065 284 | 200,50,13,10,4,8.328290471396 285 | 200,50,13,10,9,7.965597586948 286 | 200,50,13,11.3,1,7.682769675656 287 | 200,50,13,11.3,4,7.487133133785 288 | 200,50,13,11.3,9,7.161072230667 289 | 200,50,13,13,1,6.209222134635 290 | 200,50,13,13,4,6.051108485858 291 | 200,50,13,13,9,5.787585737897 292 | 200,50,12.7,0.3,1,1.716666666667 293 | 200,50,12.7,0.3,4,6.866666666667 294 | 200,50,12.7,0.3,9,15.45 295 | 200,50,12.7,1,1,1.716666666667 296 | 200,50,12.7,1,4,6.866666666667 297 | 200,50,12.7,1,9,15.45 298 | 200,50,12.7,1.7,1,1.716666666667 299 | 200,50,12.7,1.7,4,6.866666666667 300 | 200,50,12.7,1.7,9,15.45 301 | 200,50,12.7,2,1,20.42318333333 302 | 200,50,12.7,2,4,19.89273333333 303 | 200,50,12.7,2,9,19.00865 304 | 200,50,12.7,10,1,8.55978744039 305 | 200,50,12.7,10,4,8.337464643123 306 | 200,50,12.7,10,9,7.966926647679 307 | 200,50,12.7,11.3,1,7.67812933403 308 | 200,50,12.7,11.3,4,7.478705784882 309 | 200,50,12.7,11.3,9,7.146333202968 310 | 200,50,40,0.3,1,0.5666666666667 311 | 200,50,40,0.3,4,2.266666666667 312 | 200,50,40,0.3,9,5.1 313 | 200,50,40,1,1,0.5666666666667 314 | 200,50,40,1,4,2.266666666667 315 | 200,50,40,1,9,5.1 316 | 200,50,40,1.7,1,0.5666666666667 317 | 200,50,40,1.7,4,2.266666666667 318 | 200,50,40,1.7,9,5.1 319 | 200,50,40,2,1,6.780733333333 320 | 200,50,40,2,4,6.722933333333 321 | 200,50,40,2,9,6.6266 322 | 200,50,40,10,1,5.141545417733 323 | 200,50,40,10,4,5.097718104294 324 | 200,50,40,10,9,5.024672581896 325 | 200,50,40,11.3,1,4.96673287353 326 | 200,50,40,11.3,4,4.924395688748 327 | 200,50,40,11.3,9,4.853833714111 328 | 200,50,40,13,1,4.634736581331 329 | 200,50,40,13,4,4.595229383329 330 | 200,50,40,13,9,4.529384053325 331 | 200,0,1,0.3,1,16.66666666667 332 | 200,0,1,0.3,4,66.66666666667 333 | 200,0,1,0.3,9,150 334 | 200,0,1,1,1,16.66666666667 335 | 200,0,1,1,4,66.66666666667 336 | 200,0,1,1,9,150 337 | 200,0,13,0.3,1,16.66666666667 338 | 200,0,13,0.3,4,66.66666666667 339 | 200,0,13,0.3,9,150 340 | 200,0,13,1,1,16.66666666667 341 | 200,0,13,1,4,66.66666666667 342 | 200,0,13,1,9,150 343 | 200,0,13,1.7,1,16.66666666667 344 | 200,0,13,1.7,4,66.66666666667 345 | 200,0,13,1.7,9,150 346 | 200,0,13,2,1,183.3333333333 347 | 200,0,13,2,4,133.3333333333 348 | 200,0,13,2,9,50 349 | 200,0,13,10,1,0 350 | 200,0,13,10,4,0 351 | 200,0,13,10,9,0 352 | 200,0,13,11.3,1,0 353 | 200,0,13,11.3,4,0 354 | 200,0,13,11.3,9,0 355 | 200,0,13,13,1,0 356 | 200,0,13,13,4,0 357 | 200,0,13,13,9,0 358 | 200,0,12.7,0.3,1,16.66666666667 359 | 200,0,12.7,0.3,4,66.66666666667 360 | 200,0,12.7,0.3,9,150 361 | 200,0,12.7,1,1,16.66666666667 362 | 200,0,12.7,1,4,66.66666666667 363 | 200,0,12.7,1,9,150 364 | 200,0,12.7,1.7,1,16.66666666667 365 | 200,0,12.7,1.7,4,66.66666666667 366 | 200,0,12.7,1.7,9,150 367 | 200,0,12.7,2,1,183.3333333333 368 | 200,0,12.7,2,4,133.3333333333 369 | 200,0,12.7,2,9,50 370 | 200,0,12.7,10,1,0 371 | 200,0,12.7,10,4,0 372 | 200,0,12.7,10,9,0 373 | 200,0,12.7,11.3,1,0 374 | 200,0,12.7,11.3,4,0 375 | 200,0,12.7,11.3,9,0 376 | 200,0,40,0.3,1,16.66666666667 377 | 200,0,40,0.3,4,66.66666666667 378 | 200,0,40,0.3,9,150 379 | 200,0,40,1,1,16.66666666667 380 | 200,0,40,1,4,66.66666666667 381 | 200,0,40,1,9,150 382 | 200,0,40,1.7,1,16.66666666667 383 | 200,0,40,1.7,4,66.66666666667 384 | 200,0,40,1.7,9,150 385 | 200,0,40,2,1,183.3333333333 386 | 200,0,40,2,4,133.3333333333 387 | 200,0,40,2,9,50 388 | 200,0,40,10,1,0 389 | 200,0,40,10,4,0 390 | 200,0,40,10,9,0 391 | 200,0,40,11.3,1,0 392 | 200,0,40,11.3,4,0 393 | 200,0,40,11.3,9,0 394 | 200,0,40,13,1,0 395 | 200,0,40,13,4,0 396 | 200,0,40,13,9,0 397 | 122,12,12,2,3,20.527232 398 | 122,12,12,2,3,20.527232 399 | --------------------------------------------------------------------------------