Copy disabled (too large)
Download .txt
Showing preview only (20,046K chars total). Download the full file to get everything.
Repository: fsprojects/ExcelFinancialFunctions
Branch: master
Commit: 97756a375769
Files: 104
Total size: 19.1 MB
Directory structure:
gitextract_v0ky_s4e/
├── .config/
│ └── dotnet-tools.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── pull-requests.yml
│ ├── push-master.yml
│ └── release.yml
├── .gitignore
├── ExcelFinancialFunctions.sln
├── LICENSE.txt
├── PackageReadmeFile.md
├── README.md
├── RELEASE_NOTES.md
├── docs/
│ ├── compatibility.fsx
│ ├── index.fsx
│ └── openofficediff.fsx
├── src/
│ └── ExcelFinancialFunctions/
│ ├── ExcelFinancialFunctions.fsproj
│ ├── ExcelFinancialFunctions.snk
│ ├── bonds.fs
│ ├── common.fs
│ ├── daycountbasis.fs
│ ├── depreciation.fs
│ ├── irr.fs
│ ├── loan.fs
│ ├── misc.fs
│ ├── oddbonds.fs
│ ├── publicenums.fs
│ ├── tbill.fs
│ ├── testpreconditions.fs
│ ├── tvm.fs
│ └── wrapperdotnettype.fs
└── tests/
├── ExcelFinancialFunctions.ConsoleTests/
│ ├── ExcelFinancialFunctions.ConsoleTests.fsproj
│ ├── MatrixTests.fs
│ ├── README.md
│ ├── SpotTests.fs
│ ├── excel.fs
│ ├── testinfrastructure.fs
│ └── testsdef.fs
├── ExcelFinancialFunctions.ReleaseTests/
│ ├── ExcelFinancialFunctions.ReleaseTests.csproj
│ ├── README.md
│ └── SpotTests.cs
└── ExcelFinancialFunctions.Tests/
├── ExcelFinancialFunctions.Tests.fsproj
├── Program.fs
├── concurrencytests.fs
├── crosstests.fs
├── spottests.fs
├── testdata/
│ ├── accrint.test
│ ├── accrintm.test
│ ├── amordegrc.test
│ ├── amorlinc.test
│ ├── coupdays.test
│ ├── coupdaysbs.test
│ ├── coupdaysnc.test
│ ├── coupncd.test
│ ├── coupnum.test
│ ├── couppcd.test
│ ├── cumipmt.test
│ ├── cumprinc.test
│ ├── db.test
│ ├── ddb.test
│ ├── disc.test
│ ├── dollarde.test
│ ├── dollarfr.test
│ ├── duration.test
│ ├── effect.test
│ ├── fv.test
│ ├── fvschedule.test
│ ├── intrate.test
│ ├── ipmt.test
│ ├── irr.test
│ ├── ispmt.test
│ ├── mduration.test
│ ├── mirr.test
│ ├── nominal.test
│ ├── nper.test
│ ├── npv.test
│ ├── oddfprice.test
│ ├── oddfyield.test
│ ├── oddlprice.test
│ ├── oddlyield.test
│ ├── pduration.csv
│ ├── pmt.test
│ ├── ppmt.test
│ ├── price.test
│ ├── pricedisc.test
│ ├── pricemat.test
│ ├── pv.test
│ ├── rate.test
│ ├── received.test
│ ├── rri.csv
│ ├── sln.test
│ ├── syd.test
│ ├── tbilleq.test
│ ├── tbillprice.test
│ ├── tbillyield.test
│ ├── testdata.xlsx
│ ├── vdb.test
│ ├── xirr.test
│ ├── yearfrac.test
│ ├── yielddisc.test
│ ├── yieldmat.test
│ ├── yieldnegative.csv
│ └── yieldnegativefails.csv
└── testutils.fs
================================================
FILE CONTENTS
================================================
================================================
FILE: .config/dotnet-tools.json
================================================
{
"version": 1,
"isRoot": true,
"tools": {
"fsdocs-tool": {
"version": "20.0.0",
"commands": [
"fsdocs"
]
}
}
}
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .github/ISSUE_TEMPLATE.md
================================================
### Description
Please provide a succinct description of your issue.
### Repro steps
Please provide the steps required to reproduce the problem
1. Step A
2. Step B
### Expected behavior
Please provide a description of the behaviour you expect.
### Actual behavior
Please provide a description of the actual behaviour you observe.
### Known workarounds
Please provide a description of any known workarounds.
### Related information
* Operating system
* Branch
* .NET Runtime, CoreCLR or Mono Version
* Performance information, links to performance testing scripts
================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
================================================
FILE: .github/workflows/pull-requests.yml
================================================
name: Pull Request
on:
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Run Tests
run: dotnet test --no-build --verbosity normal
================================================
FILE: .github/workflows/push-master.yml
================================================
name: Build+Test+Docs
on:
push:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build Debug
run: dotnet build --no-restore
- name: Run Debug Tests (Release tests fail to discover)
run: dotnet test --no-build --verbosity normal
- name: Build Release
run: dotnet build --no-restore --configuration=Release
- name: Restore tools
run: dotnet tool restore
- name: Run fsdocs
run: dotnet fsdocs build --eval --strict --properties Configuration=Release
- name: Deploy docs
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./output
publish_branch: gh-pages
force_orphan: true
================================================
FILE: .github/workflows/release.yml
================================================
name: Release
on:
release:
types:
- published
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
env:
FSPROJ: "src\\ExcelFinancialFunctions\\ExcelFinancialFunctions.fsproj"
steps:
- uses: actions/checkout@v2
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
dotnet-version: 6.0.x
- name: Restore dependencies
run: dotnet restore $FSPROJ
- name: Build Release
run: dotnet build $FSPROJ /p:Configuration=Release --no-restore --verbosity normal
- name: Create NuGet package
run: dotnet pack $FSPROJ /p:Configuration=Release /p:GitVersion=${GITHUB_REF#refs/tags/} /p:ReleaseNotes="${{ github.event.release.body }}" --no-build --verbosity normal
- name: Publish package to NuGet Gallery (if this version not published before)
run: dotnet nuget push **\*.nupkg -s https://api.nuget.org/v3/index.json -k ${{ secrets.NUGET_ORG_TOKEN }} --skip-duplicate
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
# Xamarin Studio / monodevelop user-specific
*.userprefs
*.dll.mdb
*.exe.mdb
# Build results
[Dd]ebug/
[Rr]elease/
x64/
build/
[Bb]in/
[Oo]bj/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
*.cachefile
# Visual Studio profiler
*.psess
*.vsp
*.vspx
# Other Visual Studio data
.vs/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.Publish.xml
# Enable nuget.exe in the .nuget folder (though normally executables are not tracked)
!.nuget/NuGet.exe
# Windows Azure Build Output
csx
*.build.csdef
# Windows Store app package directory
AppPackages/
# Others
sql/
*.Cache
ClientBin/
[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
*.[Pp]ublish.xml
*.pfx
*.publishsettings
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file to a newer
# Visual Studio version. Backup files are not needed, because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
App_Data/*.mdf
App_Data/*.ldf
#LightSwitch generated files
GeneratedArtifacts/
_Pvt_Extensions/
ModelManifest.xml
# =========================
# Windows detritus
# =========================
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
# ===================================================
# Exclude F# project specific directories and files
# ===================================================
# NuGet Packages Directory
packages/
# Generated documentation folder
docs/output/
# Temp folder used for publishing docs
temp/
# Test results produced by build
TestResults.xml
# Nuget outputs
nuget/*.nupkg
release.cmd
release.sh
localpackages/
paket-files
*.orig
.paket/paket.exe
docs/content/license.md
docs/content/release-notes.md
.fake
docs/tools/FSharp.Formatting.svclog
tmp
.ionide
.fsdocs
output
================================================
FILE: ExcelFinancialFunctions.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31702.278
MinimumVisualStudioVersion = 10.0.40219.1
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ExcelFinancialFunctions", "src\ExcelFinancialFunctions\ExcelFinancialFunctions.fsproj", "{8853070C-D0B3-4DE6-B80B-B1D10F839359}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{7F0B1813-150B-4B30-8872-625C37BD67D4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{5E196AFF-81D3-4BD5-964A-5A9E642CD104}"
ProjectSection(SolutionItems) = preProject
docs\content\compatibility.fsx = docs\content\compatibility.fsx
docs\content\index.fsx = docs\content\index.fsx
docs\content\openofficediff.fsx = docs\content\openofficediff.fsx
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{6FEE0C58-7CBD-4091-8D56-55AA733E1131}"
ProjectSection(SolutionItems) = preProject
docs\tools\generate.fsx = docs\tools\generate.fsx
docs\tools\templates\template.cshtml = docs\tools\templates\template.cshtml
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "project", "project", "{6A5078DA-C727-406C-B792-ECC502BAA3D9}"
ProjectSection(SolutionItems) = preProject
build.fsx = build.fsx
README.md = README.md
RELEASE_NOTES.md = RELEASE_NOTES.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{499DEF1A-0386-4DD9-B276-3CCC6002639E}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A38472EB-1686-402D-8619-EC2BA407C111}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ExcelFinancialFunctions.Tests", "tests\ExcelFinancialFunctions.Tests\ExcelFinancialFunctions.Tests.fsproj", "{17FE313F-5304-448C-892E-451E17EEEE83}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8853070C-D0B3-4DE6-B80B-B1D10F839359}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8853070C-D0B3-4DE6-B80B-B1D10F839359}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8853070C-D0B3-4DE6-B80B-B1D10F839359}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8853070C-D0B3-4DE6-B80B-B1D10F839359}.Release|Any CPU.Build.0 = Release|Any CPU
{17FE313F-5304-448C-892E-451E17EEEE83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17FE313F-5304-448C-892E-451E17EEEE83}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17FE313F-5304-448C-892E-451E17EEEE83}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17FE313F-5304-448C-892E-451E17EEEE83}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{8853070C-D0B3-4DE6-B80B-B1D10F839359} = {499DEF1A-0386-4DD9-B276-3CCC6002639E}
{5E196AFF-81D3-4BD5-964A-5A9E642CD104} = {7F0B1813-150B-4B30-8872-625C37BD67D4}
{6FEE0C58-7CBD-4091-8D56-55AA733E1131} = {7F0B1813-150B-4B30-8872-625C37BD67D4}
{17FE313F-5304-448C-892E-451E17EEEE83} = {A38472EB-1686-402D-8619-EC2BA407C111}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F7A0269D-790B-4C1C-807C-7415B62CD59B}
EndGlobalSection
EndGlobal
================================================
FILE: LICENSE.txt
================================================
Copyright 2010-2013
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Apache License, Version 2.0
===========================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
------------------------------------------------------------
### TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
**1. Definitions.**
- "License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
- "Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
- "Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
- "You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
- "Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
- "Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
- "Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
- "Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
- "Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
- "Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
**2. Grant of Copyright License.**
Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
**3. Grant of Patent License.**
Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
**4. Redistribution.**
You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
- You must give any other recipients of the Work or
Derivative Works a copy of this License; and
- You must cause any modified files to carry prominent notices
stating that You changed the files; and
- You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
- If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
**5. Submission of Contributions.**
Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
**6. Trademarks.**
This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
**7. Disclaimer of Warranty.**
Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
**8. Limitation of Liability.**
In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
**9. Accepting Warranty or Additional Liability.**
While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
================================================
FILE: PackageReadmeFile.md
================================================
# Excel Financial Functions
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.
[](https://www.nuget.org/packages/ExcelFinancialFunctions/)
[](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml)
## Goal: Match Excel
We replicate the results Excel would produce in every situation,
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.
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.
### Thoroughly tested
As of last count, the library is validated against 199,252 test cases.
* **ExcelFinancialFunctions.Tests**: Unit tests checking against previously-determined truth values from Excel 2010. Inputs and expected outputs are read from data files.
* **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.
### You can help!
Found a discrepency? [Open an Issue](https://github.com/fsprojects/ExcelFinancialFunctions/issues)! Or better yet, a [Pull Request](https://github.com/fsprojects/ExcelFinancialFunctions/pulls).
## Adding it to your project
Excel Financial Functions is a .NET Standard 2.0 library, which you can add to any project
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.
Simply add it from NuGet in the usual way:
```
PS> dotnet add package ExcelFinancialFunctions
Determining projects to restore...
info : Adding PackageReference for package 'ExcelFinancialFunctions' into project
info : GET https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json
info : OK https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 69ms
info : Restoring packages for project.csproj...
info : PackageReference for package 'ExcelFinancialFunctions' version '2.4.1' added to file 'project.csproj'.
info : Committing restore...
log : Restored project.csproj (in 72 ms).
```
## Using it
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.
``` c#
using Excel.FinancialFunctions;
Console.WriteLine( Financial.IPmt(rate: 0.005, per: 53, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) );
// Displays -796.3747578439793
Console.WriteLine( Financial.Pmt(rate: 0.005, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) );
// Displays -1687.7136560969248
```
Or from F#:
```F#
open Excel.FinancialFunctions
printfn "%f" <| Financial.IPmt (0.005, 53., 180., 200000., 0., PaymentDue.EndOfPeriod)
// Displays -796.374758
printfn "%f" <| Financial.Pmt (0.005, 180., 200000., 0., PaymentDue.EndOfPeriod)
// Displays -1687.713656
```
================================================
FILE: README.md
================================================
# Excel Financial Functions
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.
[](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml)
[](https://www.nuget.org/packages/ExcelFinancialFunctions/)
## Goal: Match Excel
We replicate the results Excel would produce in every situation,
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.
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.
### Thoroughly tested
As of last count, the library is validated against 199,252 test cases.
* [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.
* [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.
### Difference #1: CoupDays
There are two notable areas where we judged that Excel was sufficiently incorrect
such that we needed to deviate from the primary goal of matching Excel precisely.
The first is the coupDays algorithm. Excel doesn't respect the following:
```
coupDays = coupDaysBS + coupDaysNC.
```
This equality should stand. The result differs from Excel by +/- one or two days when the date spans a leap year.
### Difference #2: VDB
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).
```
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)
```
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.
### You can help!
Found a discrepency? [Open an Issue](https://github.com/fsprojects/ExcelFinancialFunctions/issues)! Or better yet, a [Pull Request](https://github.com/fsprojects/ExcelFinancialFunctions/pulls).
## Adding it to your project
Excel Financial Functions is a .NET Standard 2.0 library, which you can add to any project
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.
Simply add it from NuGet in the usual way:
```
PS> dotnet add package ExcelFinancialFunctions
Determining projects to restore...
info : Adding PackageReference for package 'ExcelFinancialFunctions' into project
info : GET https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json
info : OK https://api.nuget.org/v3/registration5-gz-semver2/excelfinancialfunctions/index.json 69ms
info : Restoring packages for project.csproj...
info : PackageReference for package 'ExcelFinancialFunctions' version '2.4.1' added to file 'project.csproj'.
info : Committing restore...
log : Restored project.csproj (in 72 ms).
```
## Using it
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.
``` c#
using Excel.FinancialFunctions;
Console.WriteLine( Financial.IPmt(rate: 0.005, per: 53, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) );
// Displays -796.3747578439793
Console.WriteLine( Financial.Pmt(rate: 0.005, nper: 180, pv: 200000, fv: 0, typ: PaymentDue.EndOfPeriod) );
// Displays -1687.7136560969248
```
Or from F#:
```F#
open Excel.FinancialFunctions
printfn "%f" <| Financial.IPmt (0.005, 53., 180., 200000., 0., PaymentDue.EndOfPeriod)
// Displays -796.374758
printfn "%f" <| Financial.Pmt (0.005, 180., 200000., 0., PaymentDue.EndOfPeriod)
// Displays -1687.713656
```
## Code of Conduct
This repository is governed by the [Contributor Covenant Code of Conduct](https://www.contributor-covenant.org/).
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.
## Library license
The library is available under Apache 2.0. For more information see the [License file](./LICENSE.txt).
## The origin
The original author is [Luca Bolognese](https://github.com/lucabol). His story:
> 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.
>
> 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.
>
> I am happy that the tests still pass! This is a testament to good backward compatibility in the language.
>
> We think this is one of the most Excel compliant libraries in existence because of the extensive test-suite.
## Maintainers
Current maintainer is [James Coliz](https://github.com/jcoliz).
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)?
The default maintainer account for projects under "fsprojects" is [@fsprojectsgit](https://github.com/fsprojectsgit) - F# Community Project Incubation Space (repo management)
================================================
FILE: RELEASE_NOTES.md
================================================
## 3.2.0 - May 10 2022
* Removes needless constraint on 0-value inputs to FV & PMT functions. (PR #67)
## 3.1.0 - Dec 20 2021
* Adds PDURATION function. Returns the number of periods required by an investment to reach a specified value. (Resolves #62)
* Adds RRI function. Also used for CAGR. Returns an equivalent interest rate for the growth of an investment. (Resolves #60)
* Improves XIRR function by reducing the precision required before an answer is returned. (Fixes #27)
* Improves ACCRINT function by allowing first interest date on the settlement date. (Fixes #22)
* 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)
## 3.0.0 - Dec 7 2021
* Retarget library onto .NET Standard 2.0
* Adds explicit support for .NET Core 2.0 and higher including 5.0 and 6.0
* Removes support for full .NET Framework 4.6 and lower
## 2.4.1
* Relaxed FSharp.Core dependency
## 2.4
* Only build profile 259 portable profile of the library (net40 not needed)
## 2.3
* Portable version of the library
## 2.2.1
* Price and yield functions now accept rate = 0
## 2.2
* Rename the top-level namespace to `Excel.FinancialFunctions`
## 2.1
* Move to github
## 2.0
* Fixed order of parameter and naming to the Rate function
## 1.0
* Fixed call to throw in bisection
* Changed findBounds algo
* Added TestXirrBugs function
* Removed the NewValue functions everywhere
================================================
FILE: docs/compatibility.fsx
================================================
(*** hide ***)
#I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0"
#r "ExcelFinancialFunctions.dll"
open System
open Excel.FinancialFunctions
(**
Compatibility
=============
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.
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).
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.
However, there're still some differences comparing to Excel.
_Read more about OpenOffice vs Excel [here](openofficediff.html)._
COUPDAYS
--------
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))
*)
let settlement = DateTime(2012, 1, 1)
let maturity = DateTime(2016, 2, 29)
let param = settlement, maturity, Frequency.SemiAnnual, DayCountBasis.ActualActual
let days = Financial.CoupDays param
let bs = Financial.CoupDaysBS param
let nc = Financial.CoupDaysNC param
// Excel: 2
days - bs - nc
// [fsi:val days : float = 182.0]
// [fsi:val bs : float = 123.0]
// [fsi:val nc : float = 59.0]
// [fsi:val it : float = 0.0]
(**
VDB
---
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)
Notice that in Excel by using '1' (no_switch) instead of '0' as the last parameter everything works as expected.
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.
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))
*)
let vdb sp ep switch =
Financial.Vdb(100.0, 10.0, 13.0, sp, ep, 1.0,
if switch then VdbSwitch.SwitchToStraightLine else VdbSwitch.DontSwitchToStraightLine)
let p1 = vdb 0.0 0.5 false
let p2 = vdb 0.5 1.0 false
let total = vdb 0.0 1.0 false
// Excel: 0.1479
total - p1 - p2
// [fsi:val p1 : float = 3.846153846]
// [fsi:val p2 : float = 3.846153846]
// [fsi:val total : float = 7.692307692]
// [fsi:val it : float = 0.0]
let p1sw = vdb 0.0 0.5 true
let p2sw = vdb 0.5 1.0 true
let totalsw = vdb 0.0 1.0 true
// Excel: 0.0000
totalsw - p1sw - p2sw
// [fsi:val p1sw : float = 3.846153846]
// [fsi:val p2sw : float = 3.846153846]
// [fsi:val totalsw : float = 7.692307692]
// [fsi:val it : float = 0.0]
(**
AMORDEGRC
---------
ExcelCompliant is used because Excel stores 13 digits. AmorDegrc algorithm rounds numbers
and returns different results unless the numbers get rounded to 13 digits before rounding them.
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))
*)
let amorDegrc excelCompliant =
Financial.AmorDegrc(100.0, DateTime(2014,1,1), DateTime(2016,1,1),
50.0, 1.0, 0.3, DayCountBasis.ActualActual, excelCompliant)
amorDegrc true
// [fsi:val it : float = 23.0]
amorDegrc false
// [fsi:val it : float = 22.0]
(**
DDB
---
Excel Ddb has two interesting characteristics:
1. It special cases ddb for fractional periods between 0 and 1 by considering them to be 1
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.
This function makes the same 1. adjustment.([office docs](http://office.microsoft.com/en-us/excel/HP052090511033.aspx))
*)
(**
RATE and ODDFYIELD
------------------
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))
*)
(**
XIRR and XNPV
-------------
XIRR and XNPV functions are related: the net present value, given the internal rate of return, should be zero.
However, XNPV works only for positive rates even though the XIRR results might be negative.
The results can also be different because of the root finding functions. ([office docs](http://office.microsoft.com/en-us/excel/HP052093411033.aspx))
*)
let dates = [|DateTime(2000, 2, 29); DateTime(2000, 3, 31)|]
let values = [|206101714.849377; -156650972.54265|]
// Excel: -0.960452189
Financial.XIrr(values, dates, -0.1)
// [fsi:val it : float = -0.960452195]
// Excel: #NUM!
Financial.XNpv(-0.960452195, values, dates)
// [fsi:val it : float = -0.008917063475]
// Excel: #NUM!
Financial.XNpv(-0.960452189, values, dates)
// [fsi:val it : float = 2.646784514]
let values2 = [|15108163.3840923; -75382259.6628424|]
// Excel: #NUM!
Financial.XIrr(values2, dates, -0.1)
// [fsi:val it : float = 165601346.1]
// Excel: 165601345.6
Financial.XIrr(values2, dates, 0.1)
// [fsi:val it : float = 165601346.1]
Financial.XNpv(165601346.1, values2, dates)
// [fsi:val it : float = -0.000269997865]
Financial.XNpv(165601345.6, values2, dates)
// [fsi:val it : float = -0.004144238308]
================================================
FILE: docs/index.fsx
================================================
(*** hide ***)
// This block of code is omitted in the generated HTML documentation. Use
// it to define helpers that you do not want to show in the documentation.
#I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0"
(**
Excel Financial Functions
===================
This is a .NET library that provides the full set of financial functions from Excel.
It can be used from both F# and C# as well as from other .NET languages.
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.
The package is available on <a href="https://nuget.org/packages/ExcelFinancialFunctions">NuGet</a>. [](https://www.nuget.org/packages/ExcelFinancialFunctions/)
You can also use `ExcelFinancialFunctions` in [dotnet interactive](https://github.com/dotnet/interactive)
notebooks, in [Visual Studio Code](https://code.visualstudio.com/)
or [Jupyter](https://jupyter.org/), or in F# scripts (`.fsx` files),
by referencing the package as follows:
#r "nuget: ExcelFinancialFunctions" // Use the latest version
Example
-------
This example demonstrates using the YIELD function to calculate bond yield.
*)
#r "ExcelFinancialFunctions.dll"
open System
open Excel.FinancialFunctions
// returns 0.065 or 6.5%
Financial.Yield (DateTime(2008,2,15), DateTime(2016,11,15), 0.0575, 95.04287, 100.0,
Frequency.SemiAnnual, DayCountBasis.UsPsa30_360)
(**
Samples & documentation
-----------------------
The library comes with comprehensible documentation. The tutorials and articles are
automatically generated from `*.fsx` files in [the docs folder][docs]. The API
reference is automatically generated from Markdown comments in the library implementation.
* [API Reference](reference/index.html) contains automatically generated documentation for all types, modules
and functions in the library. This includes the links to the Excel documentation.
* [Excel Compatibility](compatibility.html) section explains the possible differences with Excel's results.
Contributing and copyright
--------------------------
The project is hosted on [GitHub][gh] where you can [report issues][issues], fork
the project and submit pull requests. If you're adding new public API, please also
consider adding [samples][content] that can be turned into a documentation.
The library was originally developed by Luca Bolognese, the initial version can be
downloaded [here][msdn]. It is available under Apache License, for more information
see the [License file][license] in the GitHub repository.
[content]: https://github.com/fsprojects/ExcelFinancialFunctions/tree/master/docs/content
[gh]: https://github.com/fsprojects/ExcelFinancialFunctions
[issues]: https://github.com/fsprojects/ExcelFinancialFunctions/issues
[readme]: https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/README.md
[license]: https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/LICENSE.txt
[msdn]: http://code.msdn.microsoft.com/office/Excel-Financial-functions-6afc7d42
*)
================================================
FILE: docs/openofficediff.fsx
================================================
(*** hide ***)
#I "../src/ExcelFinancialFunctions/bin/Release/netstandard2.0"
#r "ExcelFinancialFunctions.dll"
open System
open Excel.FinancialFunctions
(**
Difference between OpenOffice and the library
=============================================
The library was designed to be Excel-compliant (see [Compatibility](compatibility.html) section), therefore its behavior is different from OpenOffice/LibreOffice.
Most of the differences are because of the day count conventions and root finding algorithm implementation details.
Some examples are provided below.
*)
(**
ODDFYIELD, ODDFPRICE
--------------------
According to the [OO wiki](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_ODDFYIELD_function),
these functions currently return invalid results (it's #VALUE! even for valid inputs).
*)
Financial.OddFYield(DateTime(2008, 12, 11), DateTime(2021, 4, 1), DateTime(2008, 10, 15),
DateTime(2009, 4, 1), 0.06, 100., 100., Frequency.Quarterly, DayCountBasis.ActualActual)
// [fsi:Excel: 0.059976999]
Financial.OddFPrice(DateTime(1999, 2, 28), DateTime(2010, 6, 30), DateTime(1998, 2, 28),
DateTime(2009, 6, 30), 0.07, 0.03, 100., Frequency.Annual, DayCountBasis.Actual360)
// [fsi:Excel: 127.9031274]
(**
ODDLYIELD, OODLPRICE
--------------------
The functions return different results. ODDLYIELD example can be found [here](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_ODDLYIELD_function).
*)
Financial.OddLPrice(DateTime(1999, 2, 28), DateTime(2000, 2, 28), DateTime(1998, 2, 28),
0.07, 0.03, 130., Frequency.SemiAnnual, DayCountBasis.Actual360)
// [fsi:Excel: 132.8058252 LibreOffice: 132.8407748124]
Financial.OddLYield(DateTime(1990, 6, 1), DateTime(1995, 12, 31), DateTime(1990, 1, 1),
0.002, 103., 100., Frequency.Quarterly, DayCountBasis.ActualActual)
// [fsi:Excel: -0.00327563 LibreOffice: -0.002925876]
// Returns the same value even though the frequency is different
Financial.OddLYield(DateTime(1990, 6, 1), DateTime(1995, 12, 31), DateTime(1990, 1, 1),
0.002, 103., 100., Frequency.Annual, DayCountBasis.ActualActual)
// [fsi:Excel: -0.00327205 LibreOffice: -0.002925876]
(**
ACCRINT, DISC, DURATION, PRICE, YIELD, INTRATE, TBILL* and others
---------------------------------------------------------
Most likely the differences can be explained with YEARFRAC/day count implementations.
DURATION in OO is a completely different function, the analog of Excel one is called DURATION\_ADD.
*)
// in our tests, the numbers for European 30/360 basis were the same.
let accrint basis =
Financial.AccrInt(DateTime(1990, 3, 4), DateTime(1993, 3, 31),
DateTime(1992, 3, 4), 0.07, 10000., Frequency.SemiAnnual, basis)
accrint DayCountBasis.UsPsa30_360
// [fsi:Excel: 1401.944444 LibreOffice: 1400.000000]
accrint DayCountBasis.ActualActual
// [fsi:Excel: 1398.076923 LibreOffice: 1401.917808]
accrint DayCountBasis.Actual360
// [fsi:Excel: 1394.166667 LibreOffice: 1421.388889]
accrint DayCountBasis.Actual365
// [fsi:Excel: 1399.041096 LibreOffice: 1401.917808]
Financial.AccrIntM(DateTime(1990, 3, 4), DateTime(2010, 6, 5), 0.1, 12030.34, DayCountBasis.ActualActual)
// [fsi:Excel: 24367.7909 LibreOffice: 24383.68639]
Financial.Disc(DateTime(2003, 2, 14), DateTime(2004, 3, 31), 23., 100., DayCountBasis.ActualActual)
// [fsi:Excel: 0.684757 LibreOffice: 0.683820]
Financial.Duration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03,
Frequency.Annual, DayCountBasis.Actual360)
// [fsi:Excel: 8.949173 LibreOffice: 9.254729]
Financial.MDuration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03,
Frequency.SemiAnnual, DayCountBasis.Actual360)
// [fsi:Excel: 8.860247 LibreOffice: 9.158550]
Financial.Price(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 0.07, 0.1, 100.,
Frequency.Annual, DayCountBasis.Actual360)
// [fsi:Excel: 74.442516 LibreOffice: 74.334983]
Financial.PriceDisc(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 0.01, 100., DayCountBasis.ActualActual)
// [fsi:Excel: 79.966367 LibreOffice: 80.005464]
Financial.IntRate(DateTime(1980, 2, 15), DateTime(1980, 5, 4), 23., 130., DayCountBasis.UsPsa30_360)
// [fsi:Excel: 21.199780 LibreOffice: 21.471572]
Financial.TBillPrice(DateTime(1980, 2, 15), DateTime(1980, 3, 15), 2.)
// [fsi:Excel: 83.888889 LibreOffice: 82.777778]
Financial.Received(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 200., 0.01, DayCountBasis.ActualActual)
// [fsi:Excel: 250.105148 LibreOffice: 249.982925]
// the only function which seems to behave differently in Excel Office for Mac
Financial.AmorDegrc(100., DateTime(1998, 2, 28), DateTime(2000, 2, 29),
10., 0.3, 0.15, DayCountBasis.Actual365, true)
// [fsi:Excel: 0 Excel for Mac: -2 LibreOffice: 75]
(**
AMORLINC
--------
As stated [here](https://wiki.openoffice.org/wiki/Documentation/How_Tos/Calc:_AMORLINC_function),
when the date of purchase is the end of a period, Excel regards the initial period 0 as the first full period,
whereas OO regards the initial period as of zero length and returns 0.
However, there're other differences too.
*)
Financial.AmorLinc(100., DateTime(1998, 2, 28), DateTime(2000, 2, 29), 10., 0., 0.07, DayCountBasis.Actual365)
// [fsi:Excel: 14.000000 LibreOffice: 14.019178]
Financial.AmorLinc(100., DateTime(1998, 2, 28), DateTime(2009, 6, 30), 50., 1.7, 0.1, DayCountBasis.UsPsa30_360)
// [fsi:Excel: 0.0000000 LibreOffice: -63.33333]
(**
COUPDAYS, COUPDAYSBS, COUPDAYSNC
--------------------------------
In Excel the equality `coupDays = coupDaysBS + coupDaysNC` doesn't necessary hold when basis is other than Actual/Actual.
*)
let cdParam = DateTime(1980, 2, 15), DateTime(2000, 2, 28), Frequency.Annual, DayCountBasis.UsPsa30_360
Financial.CoupDays cdParam
Financial.CoupDaysBS cdParam
Financial.CoupDaysNC cdParam
// [fsi:Excel: 360 <> 345 + 13 LibreOffice: 360 = 345 + 15]
(**
CUMIPMT, CUMPRINC
-----------------
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))
*)
Financial.CumIPmt(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod)
// [fsi:Excel: -59.669577 LibreOffice: -119.669577]
Financial.CumPrinc(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod)
// [fsi:Excel: -0.8811289 LibreOffice: -1.431834]
(**
DB, DDB
-------
Seems like DDB doesn't accept fractional periods.
*)
Financial.Db(100., 10., 1., 0.3, 1.)
// [fsi:Excel: 7.5 LibreOffice: 0.0]
Financial.Ddb(100., 10., 1., 0.3, 1.)
// [fsi:Excel: 90.0 LibreOffice: Err:502]
(**
IRR, XIRR
---------
The implementation details of root finding algorithms might be the cause of differences.
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.
*)
Financial.XIrr([206101714.849377; -156650972.54265], [DateTime(2001, 2, 28); DateTime(2001, 3, 31)], -0.1)
// [fsi:Excel: -0.960452 LibreOffice: Err:502 (Invalid argument)]
================================================
FILE: src/ExcelFinancialFunctions/ExcelFinancialFunctions.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<WarnOn>3390;$(WarnOn)</WarnOn>
<Title>ExcelFinancialFunctions</Title>
<AssemblyTitle>ExcelFinancialFunctions</AssemblyTitle>
<Product>ExcelFinancialFunctions</Product>
<Description>A .NET Standard library that provides the full set of financial functions from Excel.</Description>
<Authors>Luca Bolognese</Authors>
<RepositoryUrl>https://github.com/fsprojects/ExcelFinancialFunctions</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageId>ExcelFinancialFunctions</PackageId>
<Version>$(GitVersion)</Version>
<PackageReleaseNotes>$(ReleaseNotes)</PackageReleaseNotes>
<PackageReadmeFile>PackageReadmeFile.md</PackageReadmeFile>
<PackageLicenseExpression>Apache-2.0</PackageLicenseExpression>
<FsDocsLicenseLink>https://github.com/fsprojects/ExcelFinancialFunctions/blob/master/LICENSE.txt</FsDocsLicenseLink>
<PackageIcon>logo.png</PackageIcon>
<PackageProjectUrl>https://fsprojects.github.io/ExcelFinancialFunctions</PackageProjectUrl>
<PackageTags>excel;finance;fsharp;csharp</PackageTags>
<SignAssembly>True</SignAssembly>
<AssemblyOriginatorKeyFile>ExcelFinancialFunctions.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Compile Include="publicenums.fs" />
<Compile Include="common.fs" />
<Compile Include="tvm.fs" />
<Compile Include="loan.fs" />
<Compile Include="irr.fs" />
<Compile Include="daycountbasis.fs" />
<Compile Include="bonds.fs" />
<Compile Include="depreciation.fs" />
<Compile Include="misc.fs" />
<Compile Include="oddbonds.fs" />
<Compile Include="tbill.fs" />
<Compile Include="testpreconditions.fs" />
<Compile Include="wrapperdotnettype.fs" />
<None Include="..\..\docs\img\logo.png" Pack="true" PackagePath="\"/>
<None Include="..\..\PackageReadmeFile.md" Pack="true" PackagePath="\"/>
</ItemGroup>
</Project>
================================================
FILE: src/ExcelFinancialFunctions/bonds.fs
================================================
// Bonds mathematics the Excel way
#light
namespace Excel.FinancialFunctions
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.DayCount
module internal Bonds =
// Main formulas
let accrIntM issue settlement rate par basis =
let dc = dayCount basis
let days = dc.DaysBetween issue settlement NumDenumPosition.Numerator
let daysInYear = dc.DaysInYear issue settlement
par * rate * (days/daysInYear)
let accrInt issue (Date(fiy, fim, fid) as firstInterest) settlement rate par (frequency:Frequency) basis calcMethod =
let dc = dayCount basis
let freq = float (int frequency)
let numMonths = freq2months freq
let numMonthsNeg = - numMonths
let endMonthBond = lastDayOfMonth fiy fim fid
let pcd =
if settlement > firstInterest && calcMethod = AccrIntCalcMethod.FromIssueToSettlement
then findPcdNcd firstInterest settlement numMonths basis endMonthBond |> fst
else dc.ChangeMonth firstInterest numMonthsNeg endMonthBond
let firstDate = if issue > pcd then issue else pcd
let days = dc.DaysBetween firstDate settlement NumDenumPosition.Numerator
let coupDays = dc.CoupDays pcd firstInterest freq
let aggFunction pcd ncd =
let firstDate = if issue > pcd then issue else pcd
let days =
if basis = DayCountBasis.UsPsa30_360
then
let psaMethod = if issue > pcd then Method360Us.ModifyStartDate else Method360Us.ModifyBothDates
float (dateDiff360Us firstDate ncd psaMethod)
else dc.DaysBetween firstDate ncd NumDenumPosition.Numerator
let coupDays =
if basis = DayCountBasis.UsPsa30_360
then float (dateDiff360Us pcd ncd Method360Us.ModifyBothDates)
elif basis = DayCountBasis.Actual365 then 365. / freq
else dc.DaysBetween pcd ncd NumDenumPosition.Denumerator
if issue <= pcd then float (int calcMethod) else days / coupDays
let _, _, a = datesAggregate1 pcd issue numMonthsNeg basis aggFunction (days / coupDays) endMonthBond
par * rate / freq * a
let getPriceYieldFactors settlement maturity frequency basis =
let dc = dayCount basis
let n = dc.CoupNum settlement maturity frequency
let pcd = dc.CoupPCD settlement maturity frequency
let a = dc.DaysBetween pcd settlement NumDenumPosition.Numerator
let e = dc.CoupDays settlement maturity frequency
let dsc = e - a
n, pcd, a, e, dsc
let price settlement maturity rate yld redemption frequency basis =
let n, pcd, a, e, dsc = getPriceYieldFactors settlement maturity frequency basis
let coupon = 100. * rate / frequency
let accrInt = 100. * rate / frequency * a / e
let pvFactor k = pow (1. + yld / frequency) (k - 1. + dsc / e)
let pvOfRedemption = redemption / pvFactor n
let mutable pvOfCoupons = 0.
for k = 1 to int n do pvOfCoupons <- pvOfCoupons + coupon / pvFactor (float k)
if n = 1. then
(redemption + coupon) / (1. + dsc / e * yld / frequency) - accrInt
else
pvOfRedemption + pvOfCoupons - accrInt
let yieldFunc settlement maturity rate pr redemption frequency basis =
let n, pcd, a, e, dsr = getPriceYieldFactors settlement maturity frequency basis
if n <= 1. then
let k = (redemption / 100. + rate / frequency) / (pr / 100. + (a / e * rate /frequency)) - 1.0
k * frequency * e / dsr
else
findRoot (fun yld -> price settlement maturity rate yld redemption frequency basis - pr) 0.05
let getMatFactors settlement maturity issue basis =
let dc = dayCount basis
let b = dc.DaysInYear issue settlement
let dim = dc.DaysBetween issue maturity NumDenumPosition.Numerator
let a = dc.DaysBetween issue settlement NumDenumPosition.Numerator
let dsm = dim - a
b, dim, a, dsm
let priceMat settlement maturity issue rate yld basis =
let b, dim, a, dsm = getMatFactors settlement maturity issue basis
let num1 = 100. + (dim / b * rate * 100.)
let den1 = 1. + (dsm / b * yld)
let fact2 = (a / b * rate * 100.)
num1 / den1 - fact2
let yieldMat settlement maturity issue rate pr basis =
let b, dim, a, dsm = getMatFactors settlement maturity issue basis
let term1 = dim / b * rate + 1. - pr / 100. - a / b * rate
let term2 = pr / 100. + a / b * rate
let term3 = b / dsm
term1 / term2 * term3
let getCommonFactors settlement maturity basis =
let dc = dayCount basis
let dim = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let b = dc.DaysInYear settlement maturity
dim, b
let intRate settlement maturity investment redemption basis =
let dim, b = getCommonFactors settlement maturity basis
(redemption - investment) / investment * b /dim
let received settlement maturity investment discount basis =
let dim, b = getCommonFactors settlement maturity basis
let discountFactor = discount * dim / b
// To get the following check into the precondition testing requires calculating the discountFactor twice, so I don't do it ...
// discountFactor < 1. |> elseThrow "discount * dim / b must be different from 1"
investment / ( 1. - discountFactor )
let disc settlement maturity pr redemption basis =
let dim, b = getCommonFactors settlement maturity basis
(- pr / redemption + 1.) * b / dim
let priceDisc settlement maturity discount redemption basis =
let dim, b = getCommonFactors settlement maturity basis
redemption - discount * redemption * dim / b
let yieldDisc settlement maturity pr redemption basis =
let dim, b = getCommonFactors settlement maturity basis
(redemption - pr) / pr * b / dim
let duration settlement maturity coupon yld frequency basis isMDuration =
let dc = dayCount basis
let dbc = dc.CoupDaysBS settlement maturity frequency
let e = dc.CoupDays settlement maturity frequency
let n = dc.CoupNum settlement maturity frequency
let dsc = e - dbc
let x1 = dsc / e
let x2 = x1 + n - 1.
let x3 = yld / frequency + 1.
let x4 = pow x3 x2
( x4 <> 0.) |> elseThrow "(yld / frequency + 1)^((dsc / e) + n -1) must be different from 0)"
let term1 = x2 * 100. / x4
let term3 = 100. / x4
let aggrFunction acc index =
let x5 = float index - 1. + x1
let x6 = pow x3 x5
( x6 <> 0.) |> elseThrow "x6 must be different from 0)"
let x7 = (100. * coupon / frequency) / x6
let a, b = acc
a + x7 * x5, b + x7
let term2, term4 = aggrBetween 1 (int n) aggrFunction (0., 0.)
let term5 = term1 + term2
let term6 = term3 + term4
( term6 <> 0.) |> elseThrow "term6 must be different from 0)"
if not(isMDuration) then (term5 / term6) / frequency else ((term5 / term6) / frequency) / x3
// Preconditions and special cases
let calcAccrIntM issue settlement rate par (basis:DayCountBasis) =
(settlement > issue) |> elseThrow "settlement must be after issue"
(rate > 0.) |> elseThrow "rate must be more than 0"
(par > 0.) |> elseThrow "par must be more than 0"
accrIntM issue settlement rate par basis
let calcAccrInt issue firstInterest settlement rate par (frequency:Frequency) basis (calcMethod:AccrIntCalcMethod) =
(settlement > issue) |> elseThrow "settlement must be after issue"
(firstInterest >= settlement) |> elseThrow "firstInterest must be after settlement"
(rate > 0.) |> elseThrow "rate must be more than 0"
(par > 0.) |> elseThrow "par must be more than 0"
accrInt issue firstInterest settlement rate par frequency basis calcMethod
let calcPrice settlement maturity rate yld redemption (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(rate >= 0.) |> elseThrow "rate must not be negative"
(yld > 0.) |> elseThrow "yld must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
price settlement maturity rate yld redemption (float (int frequency)) basis
let calcPriceAllowNegativeYield settlement maturity rate yld redemption (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(rate >= 0.) |> elseThrow "rate must not be negative"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
price settlement maturity rate yld redemption (float (int frequency)) basis
let calcYield settlement maturity rate pr redemption (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(rate >= 0.) |> elseThrow "rate must not be negative"
(pr > 0.) |> elseThrow "pr must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
yieldFunc settlement maturity rate pr redemption (float (int frequency)) basis
let calcPriceMat settlement maturity issue rate yld basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(maturity > issue) |> elseThrow "maturity must be after issue"
(settlement > issue) |> elseThrow "settlement must be after issue"
(rate > 0.) |> elseThrow "rate must be more than 0"
(yld > 0.) |> elseThrow "yld must be more than 0"
priceMat settlement maturity issue rate yld basis
let calcYieldMat settlement maturity issue rate pr basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(maturity > issue) |> elseThrow "maturity must be after issue"
(settlement > issue) |> elseThrow "settlement must be after issue"
(rate > 0.) |> elseThrow "rate must be more than 0"
(pr > 0.) |> elseThrow "price must be more than 0"
yieldMat settlement maturity issue rate pr basis
let calcIntRate settlement maturity investment redemption basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(investment > 0.) |> elseThrow "investment must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
intRate settlement maturity investment redemption basis
let calcReceived settlement maturity investment discount basis =
let dc = dayCount basis
let dim = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let b = dc.DaysInYear settlement maturity
let discountFactor = discount * dim / b
(discountFactor < 1.) |> elseThrow "discount * dim / b must be different from 1"
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(investment > 0.) |> elseThrow "investment must be more than 0"
(discount > 0.) |> elseThrow "redemption must be more than 0"
received settlement maturity investment discount basis
let calcDisc settlement maturity pr redemption basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(pr > 0.) |> elseThrow "investment must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
disc settlement maturity pr redemption basis
let calcPriceDisc settlement maturity discount redemption basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(discount > 0.) |> elseThrow "investment must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
priceDisc settlement maturity discount redemption basis
let calcYieldDisc settlement maturity pr redemption basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(pr > 0.) |> elseThrow "investment must be more than 0"
(redemption > 0.) |> elseThrow "redemption must be more than 0"
yieldDisc settlement maturity pr redemption basis
let calcDuration settlement maturity coupon yld (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(coupon >= 0.) |> elseThrow "coupon must be more than 0"
(yld >= 0.) |> elseThrow "yld must be more than 0"
duration settlement maturity coupon yld (float (int frequency)) basis false
let calcMDuration settlement maturity coupon yld (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(coupon >= 0.) |> elseThrow "coupon must be more than 0"
(yld >= 0.) |> elseThrow "yld must be more than 0"
duration settlement maturity coupon yld (float (int frequency)) basis true
================================================
FILE: src/ExcelFinancialFunctions/common.fs
================================================
// Common math, error management, zero finding, etc... routines used in all the rest of the library
#light
namespace Excel.FinancialFunctions
open System
open System.Collections.Generic
module internal Common =
// Error management functions
let mutable precision = 0.0001
let areEqual x y = if abs(x - y) < precision then true else false
let throw s = failwith s
let elseThrow s c = if not(c) then throw s
let raisable b p = not( (1. + b) < 0. && (p - float (int p)) <> 0. )
// Mathematical functions
let ln x = Math.Log(x)
let sign (x:float) = Math.Sign(x)
let idem x = x
let min (x:float) y = Math.Min(x, y)
let max (x:float) y = Math.Max(x, y)
let rest x = x - float (int x)
let ceiling (x:float) = Math.Ceiling(x)
let floor (x:float) = Math.Floor(x)
let pow x y = Math.Pow(x, y)
let sqr x = Math.Sqrt(x)
let log10 x = Math.Log(x, 10.)
let round excelComplaint (x:float) =
// Excel precision is of 13 digits so to be Excel compatible you need to preround to 13 digits ...
if excelComplaint then
let k = Math.Round(x, 13, MidpointRounding.AwayFromZero)
Math.Round(k, MidpointRounding.AwayFromZero)
else Math.Round(x, MidpointRounding.AwayFromZero)
// Don't want to use fold directly as it is hard to read. Building simpler utility func instead ...
let aggrBetween startPeriod endPeriod f initialValue=
let s = if startPeriod <= endPeriod then {startPeriod .. 1 .. endPeriod} else {startPeriod .. -1 .. endPeriod}
s |> Seq.fold f initialValue
// Date functions
let days (after:DateTime) (before:DateTime) = (after - before).Days
let date y m d = new DateTime(y, m, d)
let (|Date|) (d1:DateTime) = (d1.Year,d1.Month,d1.Day)
let isLeapYear (Date(y,_,_) as d) = DateTime.IsLeapYear(y)
let leapYear y = DateTime.IsLeapYear(y)
let lastDayOfMonth y m d = DateTime.DaysInMonth(y, m) = d
let lastDayOfFebruary (Date(y, m, d) as dt) = m = 2 && lastDayOfMonth y m d
let daysOfMonth y m = DateTime.DaysInMonth(y, m)
let addYears (d: DateTime) n = d.AddYears(n)
// Find an interval that bounds the root, (shift, factor, maxtTries) are guesses
let findBounds f guess minBound maxBound precision =
if guess <= minBound || guess >= maxBound then throw (sprintf "guess needs to be between %f and %f" minBound maxBound)
let shift = 0.01
let factor = 1.6
let maxTries = 60
let adjValueToMin value = if value <= minBound then minBound + precision else value
let adjValueToMax value = if value >= maxBound then maxBound - precision else value
let rec rfindBounds low up tries =
let tries = tries - 1
if tries = 0 then throw (sprintf "Not found an interval comprising the root after %i tries, last tried was (%f, %f)" maxTries low up)
let lower = adjValueToMin low
let upper = adjValueToMax up
match f lower, f upper with
| x, y when (x*y = 0.) -> lower, upper
| x, y when (x*y < 0.) -> lower, upper
| x, y when (x*y > 0.) -> rfindBounds (lower + factor * (lower - upper)) (upper + factor * (upper - lower)) tries
| x, y -> throw (sprintf "FindBounds: one of the values (%f, %f) cannot be used to evaluate the objective function" lower upper)
let low = adjValueToMin (guess - shift)
let high = adjValueToMax (guess + shift)
rfindBounds low high maxTries
// 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.
let bisection =
let maxCount = 200
let rec helper f a b count precision =
if a = b then throw (sprintf "(a=b=%f) impossible to start bisection" a)
let fa = f a
if abs fa < precision then a // a is the root
else
let fb = f b
if abs fb < precision then b // b is the root
else
let newCount = count + 1
if newCount > maxCount then throw (sprintf "No root found in %i iterations" maxCount)
if fa * fb > 0. then throw (sprintf "(%f,%f) don't bracket the root" a b)
let midvalue = a + 0.5 * (b - a)
let fmid = f midvalue
if abs fmid < precision then midvalue // the midvalue is the root
elif fa * fmid < 0. then helper f a midvalue newCount precision
elif fa * fmid > 0. then helper f midvalue b newCount precision
else throw "Bisection: It should never get here"
helper
let newton =
let maxCount = 20
let rec helper f x count precision =
let d f x = (f (x + precision) - f (x - precision))/(2. * precision)
let fx = f x
let Fx = d f x
let newX = x - (fx / Fx)
if abs (newX - x) < precision then Some( newX )
elif count > maxCount then None
else helper f newX (count + 1) precision
helper
// This is my main root finding algo. My strategy is to try a fast but not precise one (newton) first.
// If the result is sensible (it exist and has the same sign as guess), then return it, else try bisection.
// I'm sure more complex way to pick algos exist (i.e. Brent's method). But I favor simplicity here ...
let findRoot f guess =
let precision = 0.000001 // Excel precision on this, from docs
let newtValue = newton f guess 0 precision
if newtValue.IsSome && sign guess = sign newtValue.Value
then newtValue.Value
else
let lower, upper = findBounds f guess -1.0 Double.MaxValue precision
bisection f lower upper 0 precision
let memoize f =
let m = new Dictionary<_,_> ()
fun x ->
lock m (fun () ->
let foundIt, res = m.TryGetValue(x)
if foundIt then res
else
let r = f x
m.Add(x, r)
r
)
================================================
FILE: src/ExcelFinancialFunctions/daycountbasis.fs
================================================
// Messy, but excel compatible, treatment of day count conventions for bond mathematics.
// I tried to abstract out the commonality in one interface, but I have some special cases in the rest of the code
// If you want to support your own daycount, you should be ok just implementing the IDayCount interface
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
module internal DayCount =
// Some of the Excel day count info comes from http://www.dwheeler.com/yearfrac/excel-ooxml-yearfrac.pdf
type Method360Us =
| ModifyStartDate
| ModifyBothDates
type NumDenumPosition =
| Denumerator
| Numerator
// Date utility functions, not needed elsewhere, hence encapsulated in this module
let freq2months freq = 12 / int freq
let lastDayOfMonthBasis y m d basis = lastDayOfMonth y m d || (d = 30 && basis = DayCountBasis.UsPsa30_360)
let changeMonth (Date(y, m, d) as orgDate) numMonths basis returnLastDay =
let isLastDay = lastDayOfMonthBasis y m d basis
let getLastDay y m = DateTime.DaysInMonth(y, m)
let (Date(year, month, dayTemp) as newDate) = orgDate.AddMonths(numMonths)
if returnLastDay then date year month (getLastDay year month) else newDate
let noActionDates (d1:DateTime) (d2:DateTime) = 0.
let datesAggregate1 startDate endDate numMonths basis f acc returnLastMonth =
let rec iter frontDate trailingDate acc =
let stop = if numMonths > 0 then frontDate >= endDate else frontDate <= endDate
if stop then frontDate, trailingDate, acc
else
let trailingDate = frontDate
let frontDate = changeMonth frontDate numMonths basis returnLastMonth
let acc = acc + f frontDate trailingDate
iter frontDate trailingDate acc
iter startDate endDate acc
// Wasting a bit of time on the aggregation, but more concise code ...
let findPcdNcd startDate endDate numMonths basis returnLastMonth =
let pcd, ncd, _ = datesAggregate1 startDate endDate numMonths basis noActionDates 0. returnLastMonth
pcd, ncd
let findCouponDates settl (Date(my, mm, md) as mat) freq basis =
let endMonth = lastDayOfMonth my mm md
let numMonths = - freq2months freq
findPcdNcd mat settl numMonths basis endMonth
let findPreviousCouponDate settl mat freq basis =
findCouponDates settl mat freq basis |> fst
let findNextCouponDate settl mat freq basis =
findCouponDates settl mat freq basis |> snd
let numberOfCoupons settl (Date(my, mm, md) as mat) freq basis =
let (Date(pcy, pcm, pcd) as pcdate) = findPreviousCouponDate settl mat freq basis
let months = float ((my - pcy) * 12 + (mm - pcm))
months * freq / 12.
let lessOrEqualToAYearApart (Date(y1, m1, d1) as date1) (Date(y2, m2, d2) as date2) =
y1 = y2 || (y2 = y1 + 1 && (m1 > m2 || (m1 = m2 && d1 >= d2)))
let isFeb29BetweenConsecutiveYears (Date(y1, m1, d1) as date1) (Date(y2, m2, d2) as date2) =
if y1 = y2 && isLeapYear date1 then if m1 <= 2 && m2 > 2 then true else false
elif y1 = y2 then false
elif y2 = y1 + 1 then
if isLeapYear date1 then if m1 <= 2 then true else false
elif isLeapYear date2 then if m2 > 2 then true else false
else false
else throw "isFeb29BetweenConsecutiveYears: function called with non consecutive years"
let considerAsBisestile (Date(y1, m1, d1) as date1) (Date(y2, m2, d2) as date2) =
(y1 = y2 && isLeapYear date1) || (m2 = 2 && d2 = 29) || isFeb29BetweenConsecutiveYears date1 date2
let dateDiff360 sd sm sy ed em ey =
(ey - sy) * 360 + (em - sm) * 30 + (ed - sd)
let dateDiff365 (Date(sy,sm,sd) as startDate) (Date(ey,em,ed) as endDate) =
let mutable sd1, sm1, sy1, ed1, em1, ey1, startDate1, endDate1 = sd, sm, sy, ed, em, ey, startDate, endDate
if sd1 > 28 && sm1 = 2 then sd1 <- 28
if ed1 > 28 && em1 = 2 then ed1 <- 28
let startd, endd = date sy1 sm1 sd1, date ey1 em1 ed1
(ey1 - sy1) * 365 + days endd startd
let dateDiff360Us (Date(sy,sm,sd) as startDate) (Date(ey,em,ed) as endDate) method360 =
let mutable sd1, sm1, sy1, ed1, em1, ey1, startDate1, endDate1 = sd, sm, sy, ed, em, ey, startDate, endDate
if lastDayOfFebruary endDate1 && (lastDayOfFebruary startDate1 || method360 = Method360Us.ModifyBothDates)
then ed1 <- 30
if ed1 = 31 && (sd1 >= 30 || method360 = Method360Us.ModifyBothDates) then ed1 <- 30
if sd1 = 31 then sd1 <- 30
if lastDayOfFebruary startDate1 then sd1 <- 30
dateDiff360 sd1 sm1 sy1 ed1 em1 ey1
let dateDiff360Eu (Date(sy,sm,sd) as startDate) (Date(ey,em,ed) as endDate) =
let mutable sd1, sm1, sy1, ed1, em1, ey1, startDate1, endDate1 = sd, sm, sy, ed, em, ey, startDate, endDate
sd1 <- if sd1 = 31 then 30 else sd1
ed1 <- if ed1 = 31 then 30 else ed1
dateDiff360 sd1 sm1 sy1 ed1 em1 ey1
type IDayCount =
abstract CoupDays: DateTime -> DateTime -> float -> float
abstract CoupPCD: DateTime -> DateTime -> float -> DateTime
abstract CoupNCD: DateTime -> DateTime -> float -> DateTime
abstract CoupNum: DateTime -> DateTime -> float -> float
abstract CoupDaysBS: DateTime -> DateTime -> float -> float
abstract CoupDaysNC: DateTime -> DateTime -> float -> float
abstract DaysBetween: DateTime -> DateTime -> NumDenumPosition -> float
abstract DaysInYear: DateTime -> DateTime -> float
abstract ChangeMonth: DateTime -> int -> bool -> DateTime
let UsPsa30_360 () =
{ new IDayCount with
member dc.CoupDays settl mat freq =
360. / freq
member dc.CoupPCD settl mat freq =
findPreviousCouponDate settl mat freq DayCountBasis.UsPsa30_360
member dc.CoupNCD settl mat freq =
findNextCouponDate settl mat freq DayCountBasis.UsPsa30_360
member dc.CoupNum settl mat freq =
numberOfCoupons settl mat freq DayCountBasis.UsPsa30_360
member dc.CoupDaysBS settl mat freq =
float(dateDiff360Us (dc.CoupPCD settl mat freq) settl Method360Us.ModifyStartDate)
member dc.CoupDaysNC settl mat freq =
let pdc = findPreviousCouponDate settl mat freq DayCountBasis.UsPsa30_360
let ndc = findNextCouponDate settl mat freq DayCountBasis.UsPsa30_360
let totDaysInCoup = dateDiff360Us pdc ndc Method360Us.ModifyBothDates
let daysToSettl = dateDiff360Us pdc settl Method360Us.ModifyStartDate
float (totDaysInCoup - daysToSettl)
member dc.DaysBetween issue settl position =
float (dateDiff360Us issue settl Method360Us.ModifyStartDate)
member dc.DaysInYear issue settl =
360.
member dc.ChangeMonth date months returnLastDay =
changeMonth date months DayCountBasis.UsPsa30_360 returnLastDay
}
let Europ30_360 () =
{ new IDayCount with
member dc.CoupDays settl mat freq =
360. / freq
member dc.CoupPCD settl mat freq =
findPreviousCouponDate settl mat freq DayCountBasis.Europ30_360
member dc.CoupNCD settl mat freq =
findNextCouponDate settl mat freq DayCountBasis.Europ30_360
member dc.CoupNum settl mat freq =
numberOfCoupons settl mat freq DayCountBasis.Europ30_360
member dc.CoupDaysBS settl mat freq =
float(dateDiff360Eu (dc.CoupPCD settl mat freq) settl)
member dc.CoupDaysNC settl mat freq =
float(dateDiff360Eu settl (dc.CoupNCD settl mat freq))
member dc.DaysBetween issue settl position =
float (dateDiff360Eu issue settl)
member dc.DaysInYear issue settl =
360.
member dc.ChangeMonth date months returnLastDay =
changeMonth date months DayCountBasis.Europ30_360 returnLastDay
}
let Actual360 () =
{ new IDayCount with
member dc.CoupDays settl mat freq =
360. / freq
member dc.CoupPCD settl mat freq =
findPreviousCouponDate settl mat freq DayCountBasis.Actual360
member dc.CoupNCD settl mat freq =
findNextCouponDate settl mat freq DayCountBasis.Actual360
member dc.CoupNum settl mat freq =
numberOfCoupons settl mat freq DayCountBasis.Actual360
member dc.CoupDaysBS settl mat freq =
float(days settl (dc.CoupPCD settl mat freq))
member dc.CoupDaysNC settl mat freq =
float (days (dc.CoupNCD settl mat freq) settl)
member dc.DaysBetween issue settl position =
if position = NumDenumPosition.Numerator
then float (days settl issue)
else float (dateDiff360Us issue settl Method360Us.ModifyStartDate)
member dc.DaysInYear issue settl =
360.
member dc.ChangeMonth date months returnLastDay =
changeMonth date months DayCountBasis.Actual360 returnLastDay
}
let Actual365 () =
{ new IDayCount with
member dc.CoupDays settl mat freq =
365. / freq
member dc.CoupPCD settl mat freq =
findPreviousCouponDate settl mat freq DayCountBasis.Actual365
member dc.CoupNCD settl mat freq =
findNextCouponDate settl mat freq DayCountBasis.Actual365
member dc.CoupNum settl mat freq =
numberOfCoupons settl mat freq DayCountBasis.Actual365
member dc.CoupDaysBS settl mat freq =
float(days settl (dc.CoupPCD settl mat freq))
member dc.CoupDaysNC settl mat freq =
float (days (dc.CoupNCD settl mat freq) settl)
member dc.DaysBetween issue settl position =
if position = NumDenumPosition.Numerator
then float (days settl issue)
else float (dateDiff365 issue settl)
member dc.DaysInYear issue settl =
365.
member dc.ChangeMonth date months returnLastDay =
changeMonth date months DayCountBasis.Actual365 returnLastDay
}
let actualCoupDays settl mat freq =
let pcd = findPreviousCouponDate settl mat freq DayCountBasis.ActualActual
let ncd = findNextCouponDate settl mat freq DayCountBasis.ActualActual
float (days ncd pcd)
let ActualActual () =
{ new IDayCount with
member dc.CoupDays settl mat freq =
actualCoupDays settl mat freq
member dc.CoupPCD settl mat freq =
findPreviousCouponDate settl mat freq DayCountBasis.ActualActual
member dc.CoupNCD settl mat freq =
findNextCouponDate settl mat freq DayCountBasis.ActualActual
member dc.CoupNum settl mat freq =
numberOfCoupons settl mat freq DayCountBasis.ActualActual
member dc.CoupDaysBS settl mat freq =
float(days settl (dc.CoupPCD settl mat freq))
member dc.CoupDaysNC settl mat freq =
float (days (dc.CoupNCD settl mat freq) settl)
member dc.DaysBetween startDate endDate position =
float (days endDate startDate)
member dc.DaysInYear issue settl =
if not(lessOrEqualToAYearApart issue settl) then
let totYears = (settl.Year - issue.Year) + 1
let totDays = days (date (settl.Year + 1) 1 1) (date issue.Year 1 1)
float totDays / float totYears
elif considerAsBisestile issue settl then 366. else 365.
member dc.ChangeMonth date months returnLastDay =
changeMonth date months DayCountBasis.ActualActual returnLastDay
}
let dayCount = memoize (function
| DayCountBasis.UsPsa30_360 -> UsPsa30_360 ()
| DayCountBasis.ActualActual -> ActualActual ()
| DayCountBasis.Actual360 -> Actual360 ()
| DayCountBasis.Actual365 -> Actual365 ()
| DayCountBasis.Europ30_360 -> Europ30_360 ()
| _ -> throw "dayCount: it should never get here")
let calcCoupDays settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupDays settlement maturity (float (int frequency))
let calcCoupPCD settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupPCD settlement maturity (float (int frequency))
let calcCoupNCD settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupNCD settlement maturity (float (int frequency))
let calcCoupNum settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupNum settlement maturity (float (int frequency))
let calcCoupDaysBS settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupDaysBS settlement maturity (float (int frequency))
let calcCoupDaysNC settlement maturity (frequency:Frequency) basis =
maturity > settlement |> elseThrow "settlement must be before maturity"
let dc = dayCount basis
dc.CoupDaysNC settlement maturity (float (int frequency))
let calcYearFrac startDate endDate basis =
startDate < endDate |> elseThrow "startDate must be before endDate"
let dc = dayCount basis
dc.DaysBetween startDate endDate NumDenumPosition.Numerator / dc.DaysInYear startDate endDate
================================================
FILE: src/ExcelFinancialFunctions/depreciation.fs
================================================
// Depreciation calculations. AmorDegr and AmorLinc required a lot of work and trial and error. I wonder how many people are using them.
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.DayCount // because of AmorDegr and AmorLinc
module internal Depreciation =
// Main formulas
let deprRate cost salvage life = Math.Round( 1. - (( salvage / cost) ** (1. / life)), 3)
let deprForPeriod cost totDepr rate = (cost - totDepr) * rate
let deprForFirstPeriod cost rate month = cost * rate * month / 12.
let deprForLastPeriod cost totDepr rate month = (( cost - totDepr) * rate * ( 12. - month)) / 12.
let db cost salvage life period month =
let rate = deprRate cost salvage life
let rec _db totDepr per =
match int per with
| 0 ->
let depr = deprForFirstPeriod cost rate month
if int period <= 1 then depr
else _db depr (per + 1.)
| x when x = int period - 1 -> deprForPeriod cost totDepr rate
| x when x = int life - 1 -> deprForLastPeriod cost totDepr rate month
| _ ->
let depr = deprForPeriod cost totDepr rate
_db (totDepr + depr) (per + 1.)
_db 0. 0.
let sln cost salvage life = (cost - salvage) / life
let syd cost salvage life per = ((cost - salvage) * (life - per + 1.) * 2.) / (life * (life + 1.))
let totalDepr cost salvage life period factor straightLine =
let rec _ddb totDepr per =
let frac = rest period
let ddbDeprFormula totDepr = min ((cost - totDepr) * (factor / life)) ((cost - salvage - totDepr))
let slnDeprFormula totDepr aPeriod = sln (cost - totDepr) salvage (life - aPeriod)
let ddbDepr, slnDepr = ddbDeprFormula totDepr, slnDeprFormula totDepr per
let isSln = straightLine && ddbDepr < slnDepr
let depr = if isSln then slnDepr else ddbDepr
let newTotalDepr = totDepr + depr
if int period = 0 then newTotalDepr * frac
elif int per = int period - 1 then
let ddbDeprNextPeriod = ddbDeprFormula newTotalDepr
let slnDeprNextPeriod = slnDeprFormula newTotalDepr (per + 1.)
let isSlnNextPeriod = straightLine && ddbDeprNextPeriod < slnDeprNextPeriod
let deprNextPeriod =
if isSlnNextPeriod then
if int period = int life then 0.
else slnDeprNextPeriod
else
ddbDeprNextPeriod
newTotalDepr + deprNextPeriod * frac
else
_ddb newTotalDepr (per + 1.)
_ddb 0. 0.
let deprBetweenPeriods cost salvage life startPeriod endPeriod factor straightLine =
totalDepr cost salvage life endPeriod factor straightLine - totalDepr cost salvage life startPeriod factor straightLine
let ddb cost salvage life period factor =
if period >= 2.
then deprBetweenPeriods cost salvage life (period - 1.) period factor false
else totalDepr cost salvage life period factor false
let vdb cost salvage life startPeriod endPeriod factor bflag =
if bflag = VdbSwitch.DontSwitchToStraightLine
then deprBetweenPeriods cost salvage life startPeriod endPeriod factor false
else deprBetweenPeriods cost salvage life startPeriod endPeriod factor true
let daysInYear date basis =
if basis = DayCountBasis.ActualActual then
if isLeapYear date then 366. else 365.
else
let dc = dayCount basis
dc.DaysInYear date date
let firstDeprLinc cost datePurch firstP salvage rate assLife basis =
let fix29February (Date(y, m, d) as d1) =
if (basis = DayCountBasis.ActualActual || basis = DayCountBasis.Actual365) && isLeapYear d1 && m = 2 && d >= 28
then date y m 28 else d1
let dc = dayCount basis
let daysInYr = daysInYear datePurch basis
let datePurchased, firstPeriod = fix29February datePurch, fix29February firstP
let firstLen = dc.DaysBetween datePurchased firstPeriod NumDenumPosition.Numerator
let firstDeprTemp = firstLen / daysInYr * rate * cost
let firstDepr = if firstDeprTemp = 0. then cost * rate else firstDeprTemp
let assetLife = if firstDeprTemp = 0. then assLife else assLife + 1.
let availDepr = cost - salvage
if firstDepr > availDepr then availDepr, assetLife else firstDepr, assetLife
let amorLinc cost datePurchased firstPeriod salvage period rate basis =
let assetLifeTemp = ceiling (1. / rate)
let rec findDepr countedPeriod depr availDepr =
if countedPeriod > period then depr
else
let depr = if depr > availDepr then availDepr else depr
let availDeprTemp = availDepr - depr
let availDepr = if availDeprTemp < 0. then 0. else availDeprTemp
findDepr (countedPeriod + 1.) depr availDepr
if cost = salvage || period > assetLifeTemp then 0.
else
let firstDepr, _ = firstDeprLinc cost datePurchased firstPeriod salvage rate assetLifeTemp basis
if period = 0. then firstDepr
else findDepr 1. (rate * cost) (cost - salvage - firstDepr)
let deprCoeff assetLife =
let between x1 x2 = assetLife >= x1 && assetLife <= x2
if between 3. 4. then 1.5
elif between 5. 6. then 2.
elif assetLife > 6. then 2.5
else 1.
let amorDegrc cost datePurchased firstPeriod salvage period rate basis excelComplaint =
let assLife = ceiling (1. / rate)
if cost = salvage || period > assLife then 0.
else
let deprCoeff = deprCoeff assLife
let deprR = rate * deprCoeff
let firstDeprLinc, assetLife = firstDeprLinc cost datePurchased firstPeriod salvage deprR assLife basis
let firstDepr = round excelComplaint firstDeprLinc
let rec findDepr countedPeriod depr deprRate remainCost =
if countedPeriod > period then round excelComplaint depr
else
let countedPeriod = countedPeriod + 1.
let calcT = assetLife - countedPeriod
let deprTemp = if areEqual calcT 2. then remainCost * 0.5 else deprRate * remainCost
let deprRate = if areEqual calcT 2. then 1. else deprRate
let depr =
if remainCost < salvage then
if remainCost - salvage < 0. then 0. else remainCost - salvage
else deprTemp
let remainCost = remainCost - depr
findDepr countedPeriod depr deprRate remainCost
if period = 0. then firstDepr
else findDepr 1. 0. deprR (cost - firstDepr)
// Preconditions and special cases
let calcDb cost salvage life period month =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( life > 0. ) |> elseThrow "Life must be 0 or more"
( month > 0. ) |> elseThrow "Month must be 0 or more"
( period <= life ) |> elseThrow "Period must be less than life"
( period > 0. ) |> elseThrow "Period must be more than 0"
( month <= 12. ) |> elseThrow "Month must be less or equal to 12"
db cost salvage life period month
let calcSln cost salvage life =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( life > 0. ) |> elseThrow "Life must be 0 or more"
sln cost salvage life
let calcSyd cost salvage life period =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( life > 0. ) |> elseThrow "Life must be 0 or more"
( period <= life ) |> elseThrow "Period must be less than life"
( period > 0. ) |> elseThrow "Period must be more than 0"
syd cost salvage life period
let calcDdb cost salvage life period factor =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( life > 0. ) |> elseThrow "Life must be 0 or more"
( factor > 0. ) |> elseThrow "Month must be 0 or more"
( period <= life ) |> elseThrow "Period must be less than life"
( period > 0. ) |> elseThrow "Period must be more than 0"
if int period = 0 then min (cost * (factor / life)) ((cost - salvage))
else ddb cost salvage life period factor
let calcVdb cost salvage life startPeriod endPeriod factor bflag =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( life > 0. ) |> elseThrow "Life must be 0 or more"
( factor > 0. ) |> elseThrow "Month must be 0 or more"
( startPeriod <= life ) |> elseThrow "StartPeriod must be less than life"
( endPeriod <= life ) |> elseThrow "EndPeriod must be less than life"
( startPeriod <= endPeriod )|> elseThrow "StartPeriod must be less than endPeriod"
( endPeriod > 0. ) |> elseThrow "EndPeriod must be more than 0"
( bflag = VdbSwitch.DontSwitchToStraightLine || not(life = startPeriod && startPeriod = endPeriod) ) |> elseThrow "If bflag is set to SwitchToStraightLine, then life, startPeriod and endPeriod cannot all have the same value"
vdb cost salvage life startPeriod endPeriod factor bflag
let calcAmorLinc cost datePurchased firstPeriod salvage period rate basis =
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( salvage < cost ) |> elseThrow "Salvage must be less than cost"
( period >= 0. ) |> elseThrow "Period must be 0 or more"
( rate >= 0. ) |> elseThrow "Rate must be 0 or more"
(datePurchased < firstPeriod) |> elseThrow "DatePurchased must be less than FirstPeriod"
(basis <> DayCountBasis.Actual360 ) |> elseThrow "basis cannot be Actual360"
amorLinc cost datePurchased firstPeriod salvage period rate basis
let calcAmorDegrc cost datePurchased firstPeriod salvage period rate basis excelComplaint =
let assetLife = 1. / rate
let between x1 x2 = assetLife >= x1 && assetLife <= x2
( not(between 0. 3.) ) |> elseThrow "Assset life cannot be between 0 and 3"
( not(between 4. 5.) ) |> elseThrow "Assset life cannot be between 4. and 5."
( cost >= 0. ) |> elseThrow "Cost must be 0 or more"
( salvage >= 0. ) |> elseThrow "Salvage must be 0 or more"
( salvage < cost ) |> elseThrow "Salvage must be less than cost"
( period >= 0. ) |> elseThrow "Period must be 0 or more"
( rate >= 0. ) |> elseThrow "Rate must be 0 or more"
(datePurchased < firstPeriod) |> elseThrow "DatePurchased must be less than FirstPeriod"
(basis <> DayCountBasis.Actual360 ) |> elseThrow "basis cannot be Actual360"
amorDegrc cost datePurchased firstPeriod salvage period rate basis excelComplaint
================================================
FILE: src/ExcelFinancialFunctions/irr.fs
================================================
// Finding internal rate of return routines. I use a different algo then excel. The results might be different.
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
module internal Irr =
// Main formulas
let npv r cfs = cfs |> Seq.mapi (fun i cf -> cf * pvFactor r (float (i+1))) |> Seq.sumBy idem
let irr cfs guess = findRoot (fun r -> npv r cfs) guess
let mirr cfs financeRate reinvestRate =
let n = float (Seq.length cfs)
let positives = cfs |> Seq.map (fun cf -> if cf > 0. then cf else 0.)
let negatives = cfs |> Seq.map (fun cf -> if cf < 0. then cf else 0.)
(((- npv reinvestRate positives) * ((1. + reinvestRate) ** n))/
(( npv financeRate negatives) * ( 1. + financeRate ))) ** (1./(n - 1.)) - 1.
let xnpv r cfs dates =
let d0 = Seq.head dates
cfs |> Seq.map2 (fun d cf -> cf / ((1. + r) ** (float (days d d0) / 365.))) dates |> Seq.sumBy idem
let xirr cfs dates guess = findRoot (fun r -> xnpv r cfs dates) guess
// Preconditions and special cases
let validCfs cfs =
let rec _validCfs cfs pos neg =
if pos && neg then true
else match cfs with
| h::t when h > 0. -> _validCfs t true neg
| h::t when h <= 0. -> _validCfs t pos true
| [] -> false
| _ -> failwith "Should never get here"
_validCfs (Seq.toList cfs) false false
let calcIrr cfs guess =
validCfs cfs |> elseThrow "There must be one positive and one negative cash flow"
irr cfs guess
let calcNpv r cfs =
( r <> -1.) |> elseThrow "r cannot be -100%"
npv r cfs
let calcMirr cfs financeRate reinvestRate =
( financeRate <> -1.) |> elseThrow "financeRate cannot be -100%"
( reinvestRate <> -1.) |> elseThrow "reinvestRate cannot be -100%"
( Seq.length cfs <> 1) |> elseThrow "cfs must contain more than one cashflow"
( (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"
mirr cfs financeRate reinvestRate
let calcXnpv r cfs dates =
( r <> -1.) |> elseThrow "r cannot be -100%"
not(Seq.exists (fun x -> x < Seq.head dates) dates) |> elseThrow "In dates, one date is less than the first date"
(Seq.length cfs = Seq.length dates) |> elseThrow "cfs and dates must have the same length"
xnpv r cfs dates
let calcXirr cfs dates guess =
validCfs cfs |> elseThrow "There must be one positive and one negative cash flow"
not(Seq.exists (fun x -> x < Seq.head dates) dates) |> elseThrow "In dates, one date is less than the first date"
(Seq.length cfs = Seq.length dates) |> elseThrow "cfs and dates must have the same length"
xirr cfs dates guess
================================================
FILE: src/ExcelFinancialFunctions/loan.fs
================================================
// Loan related calculation routines, small variations on TVM
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
module internal Loan =
let inline approxEqual x y = abs (x - y) < 1e-10
// Main formulas
let ipmt r per nper pv fv pd =
let result = - ( pv * fvFactor r (per - 1.) * r + (pmt r nper pv fv PaymentDue.EndOfPeriod) * (fvFactor r (per - 1.) - 1.) )
if pd = PaymentDue.EndOfPeriod then result else result / (1. + r)
let ppmt r per nper pv fv pd =
pmt r nper pv fv pd - ipmt r per nper pv fv pd
let ispmt r per nper pv =
let coupon = - pv * r
coupon - (coupon / nper * per)
// Preconditions and special cases
let calcIpmt r per nper pv fv pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)"
( raisable r (per - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)"
( fv <> 0. || pv <> 0. ) |> elseThrow "fv or pv need to be different from 0"
( r > -1.) |> elseThrow "r must be more than -100%"
( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0"
( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper"
( nper > 0. ) |> elseThrow "nper must be more than 0"
if approxEqual per 1. && pd = PaymentDue.BeginningOfPeriod then 0.
elif r = -1. then -fv
else ipmt r per nper pv fv pd
let calcPpmt r per nper pv fv pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)"
( raisable r (per - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)"
( fv <> 0. || pv <> 0. ) |> elseThrow "fv or pv need to be different from 0"
( r > -1. ) |> elseThrow "r must be more than -100%"
( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0"
( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper"
( nper > 0. ) |> elseThrow "nper must be more than 0"
if approxEqual per 1. && pd = PaymentDue.BeginningOfPeriod then pmt r nper pv fv pd
elif r = -1. then 0.
else ppmt r per nper pv fv pd
let calcCumipmt r nper pv startPeriod endPeriod pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)"
( raisable r (startPeriod - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)"
( pv > 0. ) |> elseThrow "pv must be more than 0"
( r > 0. ) |> elseThrow "r must be more than 0"
( nper > 0. ) |> elseThrow "nper must be more than 0"
( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0"
( startPeriod <= endPeriod ) |> elseThrow "startPeriod must be less or equal to endPeriod"
( startPeriod >= 1. ) |> elseThrow "startPeriod must be more or equal to 1"
( endPeriod <= nper ) |> elseThrow "startPeriod and endPeriod must be less or equal to nper"
aggrBetween (int (ceiling startPeriod)) (int endPeriod) (fun acc per -> acc + calcIpmt r (float per) nper pv 0. pd) 0.
let calcCumprinc r nper pv startPeriod endPeriod pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer)"
( raisable r (startPeriod - 1.)) |> elseThrow "r is not raisable to (per - 1) (r is negative and nper not an integer)"
( pv > 0. ) |> elseThrow "pv must be more than 0"
( r > 0. ) |> elseThrow "r must be more than 0"
( nper > 0. ) |> elseThrow "nper must be more than 0"
( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0"
( startPeriod <= endPeriod ) |> elseThrow "startPeriod must be less or equal to endPeriod"
( startPeriod >= 1. ) |> elseThrow "startPeriod must be more or equal to 1"
( endPeriod <= nper ) |> elseThrow "startPeriod and endPeriod must be less or equal to nper"
aggrBetween (int (ceiling startPeriod)) (int endPeriod) (fun acc per -> acc + calcPpmt r (float per) nper pv 0. pd) 0.
let calcIspmt r per nper pv =
( per >= 1. && per <= nper ) |> elseThrow "per must be in the range 1 to nper"
( nper > 0. ) |> elseThrow "nper must be more than 0"
ispmt r per nper pv
================================================
FILE: src/ExcelFinancialFunctions/misc.fs
================================================
// Various routings that don't have an obvious classification in other files.
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
module internal Misc =
// Main formulas
let dollar fractionalDollar fraction f =
let aBase = floor fraction
let dollar = if fractionalDollar > 0. then floor fractionalDollar else ceiling fractionalDollar
let remainder = fractionalDollar - dollar
let digits = pow 10. (ceiling (log10 aBase))
f aBase dollar remainder digits
let dollarDe aBase dollar remainder digits =
remainder * digits / aBase + dollar
let dollarFr aBase dollar remainder digits =
let absDigits = abs digits
remainder * aBase / absDigits + dollar
let effect nominalRate npery =
let periods = floor npery
pow (nominalRate / periods + 1.) periods - 1.
let nominal effectRate npery =
let periods = floor npery
(pow (effectRate + 1.) (1. / periods) - 1.) * periods
// Preconditions and special cases
let calcDollarDe fractionalDollar fraction =
(fraction > 0.) |> elseThrow "fraction must be more than 0"
dollar fractionalDollar fraction dollarDe
let calcDollarFr fractionalDollar fraction =
(fraction > 0.) |> elseThrow "fraction must be more than 0"
(pow 10. (ceiling (log10 (floor fraction))) <> 0.) |> elseThrow "10^(ceiling (log10 (floor fraction))) must be different from 0"
dollar fractionalDollar fraction dollarFr
let calcEffect nominalRate npery =
(nominalRate > 0.) |> elseThrow "nominal rate must be more than zero"
(npery >= 1.) |> elseThrow "npery must be more or equal to one"
effect nominalRate npery
let calcNominal effectRate npery =
(effectRate > 0.) |> elseThrow "effective rate must be more than zero"
(npery >= 1.) |> elseThrow "npery must be more or equal to one"
nominal effectRate npery
================================================
FILE: src/ExcelFinancialFunctions/oddbonds.fs
================================================
// Difficult to digest formulas for odd bonds calculations. Trust the testcases that I got these ones right.
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.DayCount
module internal OddBonds =
// Main formulas
let coupNumber (Date(my, mm, md) as mat) (Date(sy, sm, sd) as settl) numMonths basis isWholeNumber =
let couponsTemp = if isWholeNumber then 0. else 1.
let endOfMonthTemp = lastDayOfMonth my mm md
let endOfMonth = if not(endOfMonthTemp) && mm <> 2 && md > 28 && md < daysOfMonth my mm then lastDayOfMonth sy sm sd else endOfMonthTemp
let startDate = changeMonth settl 0 basis endOfMonth
let coupons = if settl < startDate then couponsTemp + 1. else couponsTemp
let date = changeMonth startDate numMonths basis endOfMonth
let _, _, result = datesAggregate1 date mat numMonths basis (fun pcd ncd -> 1.) coupons endOfMonth
result
let daysBetweenNotNeg (dc:IDayCount) startDate endDate =
let result = dc.DaysBetween startDate endDate NumDenumPosition.Numerator
if result > 0. then result else 0.
let daysBetweenNotNegPsaHack startDate endDate =
let result = float (dateDiff360Us startDate endDate Method360Us.ModifyBothDates)
if result > 0. then result else 0.
let daysBetweenNotNegWithHack dc startDate endDate basis =
if basis = DayCountBasis.UsPsa30_360
then daysBetweenNotNegPsaHack startDate endDate
else daysBetweenNotNeg dc startDate endDate
let oddFPrice settlement (Date(my, mm, md) as maturity) issue firstCoupon rate yld redemption frequency basis =
let dc = dayCount basis
let endMonth = lastDayOfMonth my mm md
let numMonths = freq2months frequency
let numMonthsNeg = - numMonths
let e = dc.CoupDays settlement firstCoupon frequency
let n = dc.CoupNum settlement maturity frequency
let m = frequency
let dfc = daysBetweenNotNeg dc issue firstCoupon
if dfc < e then
let dsc = daysBetweenNotNeg dc settlement firstCoupon
let a = daysBetweenNotNeg dc issue settlement
let x = yld / m + 1.
let y = dsc / e
let p1 = x
let p3 = pow p1 (n - 1. + y)
let term1 = redemption / p3
let term2 = 100. * rate / m * dfc / e / pow p1 y
let term3 = aggrBetween 2 (int n) (fun acc index -> acc + 100. * rate / m / (pow p1 (float index - 1. + y))) 0.
let p2 = rate / m
let term4 = a / e * p2 * 100.
term1 + term2 + term3 - term4
else // dfc >= e
let nc = dc.CoupNum issue firstCoupon frequency
let lateCoupon = ref firstCoupon
let aggrFunction acc index =
let earlyCoupon = changeMonth !lateCoupon numMonthsNeg basis false
let nl =
if basis = DayCountBasis.ActualActual
then daysBetweenNotNeg dc earlyCoupon !lateCoupon
else e
let dci = if index > 1 then nl else daysBetweenNotNeg dc issue !lateCoupon
let startDate = if issue > earlyCoupon then issue else earlyCoupon
let endDate = if settlement < !lateCoupon then settlement else !lateCoupon
let a = daysBetweenNotNeg dc startDate endDate
lateCoupon := earlyCoupon
let dcnl, anl = acc
dcnl + dci / nl, anl + a / nl
let dcnl, anl = aggrBetween (int nc) 1 aggrFunction (0., 0.)
let dsc =
if basis = DayCountBasis.Actual360 || basis = DayCountBasis.Actual365
then
let date = dc.CoupNCD settlement firstCoupon frequency
daysBetweenNotNeg dc settlement date
else
let date = dc.CoupPCD settlement firstCoupon frequency
let a = dc.DaysBetween date settlement NumDenumPosition.Numerator
e - a
let nq = coupNumber firstCoupon settlement numMonths basis true
let n = dc.CoupNum firstCoupon maturity frequency
let x = yld / m + 1.
let y = dsc / e
let p1 = x
let p3 = pow p1 (y + nq + n)
let term1 = redemption / p3
let term2 = 100. * rate / m * dcnl / pow p1 (nq + y)
let term3 = aggrBetween 1 (int n) (fun acc index -> acc + 100. * rate / m / (pow p1 (float index + nq + y))) 0.
let term4 = 100. * rate / m * anl
term1 + term2 + term3 - term4
let oddFYield settlement maturity issue firstCoupon rate pr redemption frequency basis =
let dc = dayCount basis
let years = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let m = frequency
let px = pr - 100.
let num = rate * years * 100. - px
let denum = px / 4. + years * px / 2. + years * 100.
let guess = num / denum
findRoot (fun yld -> pr - oddFPrice settlement maturity issue firstCoupon rate yld redemption frequency basis) guess
let oddLFunc settlement maturity lastInterest rate prOrYld redemption frequency basis isLPrice =
let dc = dayCount basis
let m = frequency
let numMonths = int (12. / frequency)
let lastCoupon = lastInterest
let nc = dc.CoupNum lastCoupon maturity frequency
let earlyCoupon = ref lastCoupon
let aggrFunction acc index =
let lateCoupon = changeMonth !earlyCoupon numMonths basis false
let nl = daysBetweenNotNegWithHack dc !earlyCoupon lateCoupon basis
let dci = if index < int nc then nl else daysBetweenNotNegWithHack dc !earlyCoupon maturity basis
let a =
if lateCoupon < settlement
then dci
elif !earlyCoupon < settlement
then daysBetweenNotNeg dc !earlyCoupon settlement
else 0.
let startDate = if settlement > !earlyCoupon then settlement else !earlyCoupon
let endDate = if maturity < lateCoupon then maturity else lateCoupon
let dsc = daysBetweenNotNeg dc startDate endDate
earlyCoupon := lateCoupon
let dcnl, anl, dscnl = acc
dcnl + dci / nl, anl + a / nl , dscnl + dsc / nl
let dcnl, anl, dscnl = aggrBetween 1 (int nc) aggrFunction (0., 0., 0.)
let x = 100. * rate / m
let term1 = dcnl * x + redemption
if isLPrice then
let term2 = dscnl * prOrYld / m + 1.
let term3 = anl * x
term1 / term2 - term3
else
let term2 = anl * x + prOrYld
let term3 = m / dscnl
(term1 - term2) / term2 * term3
// Preconditions and special cases
let calcOddFPrice settlement (Date(my, mm, md) as maturity) issue firstCoupon rate yld redemption (frequency:Frequency) basis =
let endMonth = lastDayOfMonth my mm md
let numMonths = int (12. / (float(int frequency)))
let numMonthsNeg = - numMonths
let pcd, ncd = findPcdNcd (changeMonth maturity numMonthsNeg basis endMonth) firstCoupon numMonthsNeg basis endMonth
// The next condition is not in the docs, but nevertheless is needed !!!!!
(pcd = firstCoupon) |> elseThrow "maturity and firstCoupon must have the same month and day (except for February when leap years are considered)"
(maturity > firstCoupon) |> elseThrow "maturity must be after firstCoupon"
(firstCoupon > settlement) |> elseThrow "firstCoupon must be after settlement"
(settlement > issue) |> elseThrow "settlement must be after issue"
(rate >= 0.) |> elseThrow "rate must be more than 0"
(yld >= 0.) |> elseThrow "yld must be more than 0"
(redemption >= 0.) |> elseThrow "redemption must be more than 0"
oddFPrice settlement maturity issue firstCoupon rate yld redemption (float (int frequency)) basis
let calcOddFYield settlement maturity issue firstCoupon rate pr redemption (frequency:Frequency) basis =
(maturity > firstCoupon) |> elseThrow "maturity must be after firstCoupon"
(firstCoupon > settlement) |> elseThrow "firstCoupon must be after settlement"
(settlement > issue) |> elseThrow "settlement must be after issue"
(rate >= 0.) |> elseThrow "rate must be more than 0"
(pr >= 0.) |> elseThrow "pr must be more than 0"
(redemption >= 0.) |> elseThrow "redemption must be more than 0"
oddFYield settlement maturity issue firstCoupon rate pr redemption (float (int frequency)) basis
let calcOddLPrice settlement maturity lastInterest rate yld redemption (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(settlement > lastInterest) |> elseThrow "settlement must be after lastInterest"
(rate >= 0.) |> elseThrow "rate must be more than 0"
(yld >= 0.) |> elseThrow "yld must be more than 0"
(redemption >= 0.) |> elseThrow "redemption must be more than 0"
oddLFunc settlement maturity lastInterest rate yld redemption (float (int frequency)) basis true
let calcOddLYield settlement maturity lastInterest rate pr redemption (frequency:Frequency) basis =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(settlement > lastInterest) |> elseThrow "settlement must be after lastInterest"
(rate >= 0.) |> elseThrow "rate must be more than 0"
(pr >= 0.) |> elseThrow "pr must be more than 0"
(redemption >= 0.) |> elseThrow "redemption must be more than 0"
oddLFunc settlement maturity lastInterest rate pr redemption (float (int frequency)) basis false
================================================
FILE: src/ExcelFinancialFunctions/publicenums.fs
================================================
// All the enums exposed in the external API, I need to define them first becasue they are used in the internal part
#light
namespace Excel.FinancialFunctions
/// Indicates when payments are due (end/beginning of period)
type PaymentDue =
| EndOfPeriod = 0
| BeginningOfPeriod = 1
/// The type of Day Count Basis
type DayCountBasis =
/// US 30/360
| UsPsa30_360 = 0
/// Actual/Actual
| ActualActual = 1
/// Actual/360
| Actual360 = 2
/// Actual/365
| Actual365 = 3
/// European 30/360
| Europ30_360 = 4
/// The number of coupon payments per year
type Frequency =
| Annual = 1
| SemiAnnual = 2
| Quarterly = 4
/// Indicates whether accrued interest is computed from issue date (by default) or first interest to settlement
type AccrIntCalcMethod =
| FromFirstToSettlement = 0
| FromIssueToSettlement = 1
/// Specifies whether to switch to straight-line depreciation when depreciation is greater than the declining balance calculation
type VdbSwitch =
| DontSwitchToStraightLine = 1
| SwitchToStraightLine = 0
================================================
FILE: src/ExcelFinancialFunctions/tbill.fs
================================================
// Very simple TBill mathematics. The only interesting thing is the 'if' statement on line 17.
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.DayCount
module internal TBill =
// Main formulas
let getDsm settlement maturity basis =
let dc = dayCount basis
dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let TBillEq settlement maturity discount =
let dsm = getDsm settlement maturity DayCountBasis.Actual360
if dsm > 182. then
let price = (100. - discount * 100. * dsm / 360.) / 100.
let days = if dsm = 366. then 366. else 365.
let tempTerm2 = (pow (dsm / days) 2.) - (2. * dsm / days - 1.) * (1. - 1. / price)
let term2 = sqr tempTerm2
let term3 = 2. * dsm / days - 1.
2. * (term2 - dsm / days) / term3
else
// This is the algo in the docs, but it is valid just above 182 ...
365. * discount / (360. - discount * dsm)
let TBillYield settlement maturity pr =
let dsm = getDsm settlement maturity DayCountBasis.ActualActual
(100. - pr) / pr * 360. / dsm
let TBillPrice settlement maturity discount =
let dsm = getDsm settlement maturity DayCountBasis.ActualActual
100. * (1. - discount * dsm / 360.)
// Preconditions and special cases
let calcTBillEq settlement maturity discount =
let dsm = getDsm settlement maturity DayCountBasis.Actual360
let price = (100. - discount * 100. * dsm / 360.) / 100.
let days = if dsm = 366. then 366. else 365.
let tempTerm2 = (pow (dsm / days) 2.) - (2. * dsm / days - 1.) * (1. - 1. / price)
(tempTerm2 >= 0.) |> elseThrow "(dsm / days)^2 - (2. * dsm / days - 1.) * (1. - 1. / (100. - discount * 100. * dsm / 360.) / 100.) must be positive"
(2. * dsm / days - 1. <> 0.) |> elseThrow "2. * dsm / days - 1. must be different from 0"
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement"
(discount > 0.) |> elseThrow "investment must be more than 0"
TBillEq settlement maturity discount
let calcTBillYield settlement maturity pr =
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement"
(pr > 0.) |> elseThrow "pr must be more than 0"
TBillYield settlement maturity pr
let calcTBillPrice settlement maturity discount =
let dsm = getDsm settlement maturity DayCountBasis.ActualActual
(100. * (1. - discount * dsm / 360.)) > 0. |> elseThrow "a result less than zero triggers an exception"
(maturity > settlement) |> elseThrow "maturity must be after settlement"
(maturity <= (addYears settlement 1)) |> elseThrow "maturity must be less than one year after settlement"
(discount > 0.) |> elseThrow "discount must be more than 0"
TBillPrice settlement maturity discount
================================================
FILE: src/ExcelFinancialFunctions/testpreconditions.fs
================================================
namespace Excel.FinancialFunctions
[<assembly:System.Runtime.CompilerServices.InternalsVisibleTo "ExcelFinancialFunctions.ConsoleTests">]
[<assembly:System.Runtime.CompilerServices.InternalsVisibleTo "ExcelFinancialFunctions.Tests">]
do()
module internal TestPreconditions =
open System
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
open Excel.FinancialFunctions.Irr
open Excel.FinancialFunctions.DayCount
// Functions to define valid parameter combinations to testcases given that the permutations can create invalid parameters combinations
let tryPv r nper pmt fv pd =
( raisable r nper) &&
( r <> -1.) &&
( pmt <> 0. || fv <> 0. )
let tryFv r nper pmt pv pd =
( raisable r nper) &&
( r <> -1. || (r = -1. && nper > 0.) )
let tryPmt r nper pv fv pd =
( raisable r nper) &&
( r > -1. ) &&
( annuityCertainPvFactor r nper pd <> 0. )
let tryNper r pmt pv fv pd =
( r > -1.) &&
( nperFactor r pmt pv pd <> 0.) &&
( nperFactor r pmt (-fv) pd <> 0.) &&
( nperFactor r pmt (-fv) pd / nperFactor r pmt pv pd > 0.)
let tryRate nper pmt pv fv pd guess =
( pmt <> 0. || pv <> 0. ) &&
( nper > 0.) &&
not (sign pmt = sign pv && sign pv = sign fv) &&
not (sign pmt = sign pv && areEqual fv 0.) &&
not (sign pmt = sign fv && areEqual pv 0.) &&
not (sign pv = sign fv && areEqual pmt 0.)
let tryNpv r cfs = r <> -1.
let tryIrr cfs = validCfs cfs
let tryMirr cfs financeRate reinvestRate =
( financeRate <> -1.) &&
( reinvestRate <> -1.) &&
( Seq.length cfs <> 1) &&
( (npv financeRate (cfs |> Seq.map (fun cf -> if cf < 0. then cf else 0.))) <> 0. )
let tryXirr cfs dates guess =
validCfs cfs &&
not(Seq.exists (fun x -> x < Seq.head dates) dates) &&
(Seq.length cfs = Seq.length dates)
let tryDb cost salvage life period month =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( life > 0. ) &&
( month > 0. ) &&
( period <= life ) &&
( period > 0. ) &&
( month <= 12. )
let trySln cost salvage life =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( life > 0. )
let trySyd cost salvage life period =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( life > 0. ) &&
( period > 0. ) &&
( period <= life )
let tryDdb cost salvage life period factor =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( life > 0. ) &&
( factor > 0. ) &&
( period > 0. ) &&
( period <= life )
let tryVdb cost salvage life startPeriod endPeriod factor bflag =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( life > 0. ) &&
( factor > 0. ) &&
( startPeriod <= life ) &&
( endPeriod <= life ) &&
( endPeriod > 0. ) &&
( startPeriod <= endPeriod )&&
( startPeriod = float (int startPeriod )) && // This is introduced to workaround the issue with fractional startDate
( bflag = VdbSwitch.DontSwitchToStraightLine || not(life = startPeriod && startPeriod = endPeriod) )
let tryIpmt r per nper pv fv pd =
( raisable r nper) &&
( raisable r (per - 1.)) &&
( fv <> 0. || pv <> 0. ) &&
( r > -1. ) &&
( annuityCertainPvFactor r nper pd <> 0. ) &&
( per >= 1. && per <= nper ) &&
( nper > 0. )
let tryPpmt r per nper pv fv pd =
( raisable r nper) &&
( raisable r (per - 1.)) &&
( fv <> 0. || pv <> 0. ) &&
( r > -1.) &&
( annuityCertainPvFactor r nper pd <> 0. ) &&
( per >= 1. && per <= nper ) &&
( nper > 0. )
let tryCumipmt r nper pv startPeriod endPeriod pd =
( raisable r nper) &&
( raisable r (startPeriod - 1.)) &&
( pv > 0. ) &&
( r > 0. ) &&
( nper > 0. ) &&
( annuityCertainPvFactor r nper pd <> 0. ) &&
( startPeriod <= endPeriod ) &&
( endPeriod <= nper ) &&
( startPeriod >= 1. )
let tryCumprinc r nper pv startPeriod endPeriod pd =
( raisable r nper) &&
( raisable r (startPeriod - 1.)) &&
( pv > 0. ) &&
( r > 0. ) &&
( nper > 0. ) &&
( annuityCertainPvFactor r nper pd <> 0. ) &&
( startPeriod <= endPeriod ) &&
( endPeriod <= nper ) &&
( startPeriod >= 1. )
let tryIspmt r per nper pv =
( per >= 1. && per <= nper ) &&
( nper > 0. )
let tryCoupDays (settlement:DateTime) maturity (frequency:Frequency) basis =
maturity > settlement &&
[settlement.Year .. maturity.Year] |> List.exists (fun year -> DateTime.IsLeapYear(year)) |> not &&
settlement.Year <> 1993 // This is to workaround the issue with Coupdays ...
let tryCoupNum settlement maturity (frequency:Frequency) basis =
maturity > settlement
let tryCoupDaysBS settlement maturity (frequency:Frequency) basis =
maturity > settlement
let tryCoupDaysNC settlement maturity (frequency:Frequency) basis =
maturity > settlement
let tryCoupPCD settlement maturity (frequency:Frequency) basis =
maturity > settlement
let tryCoupNCD settlement maturity (frequency:Frequency) basis =
maturity > settlement
let tryAccrIntM issue settlement rate par basis =
(settlement > issue) &&
(rate > 0.) &&
(par > 0.)
let tryAccrInt issue firstInterest settlement rate par frequency basis =
(settlement > issue) &&
(firstInterest > settlement) &&
(rate > 0.) &&
(par > 0.)
let tryPrice settlement maturity rate yld redemption (frequency:Frequency) basis =
(maturity > settlement) &&
(rate > 0.) &&
(yld > 0.) &&
(redemption > 0.)
let tryYield settlement maturity rate pr redemption (frequency:Frequency) basis =
(maturity > settlement) &&
(rate > 0.) &&
(pr > 0.) &&
(redemption > 0.)
let tryPriceMat settlement maturity issue rate yld basis =
(maturity > settlement) &&
(maturity > issue) &&
(settlement > issue) &&
(rate > 0.) &&
(yld > 0.)
let tryYieldMat settlement maturity issue rate pr basis =
(maturity > settlement) &&
(maturity > issue) &&
(settlement > issue) &&
(rate > 0.) &&
(pr > 0.)
let tryYearFrac startDate endDate basis =
startDate < endDate
let tryIntRate settlement maturity investment redemption basis =
(maturity > settlement) &&
(investment > 0.) &&
(redemption > 0.)
let tryReceived settlement maturity investment discount basis =
let dc = dayCount basis
let dim = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let b = dc.DaysInYear settlement maturity
let discountFactor = discount * dim / b
discountFactor < 1. &&
(maturity > settlement) &&
(investment > 0.) &&
(discount > 0.)
let tryDisc settlement maturity pr redemption basis =
(maturity > settlement) &&
(pr > 0.) &&
(redemption > 0.)
let tryPriceDisc settlement maturity discount redemption basis =
(maturity > settlement) &&
(discount > 0.) &&
(redemption > 0.)
let tryYieldDisc settlement maturity pr redemption basis =
(maturity > settlement) &&
(pr > 0.) &&
(redemption > 0.)
let tryTBillEq settlement maturity discount =
let dc = dayCount DayCountBasis.Actual360
let dsm = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
let price = (100. - discount * 100. * dsm / 360.) / 100.
let days = if dsm = 366. then 366. else 365.
let tempTerm2 = (pow (dsm / days) 2.) - (2. * dsm / days - 1.) * (1. - 1. / price)
(tempTerm2 >= 0.) &&
(2. * dsm / days - 1. <> 0.) &&
(maturity > settlement) &&
(maturity <= (addYears settlement 1)) &&
(discount > 0.)
let tryTBillYield settlement maturity pr =
(maturity > settlement) &&
(maturity <= (addYears settlement 1)) &&
(pr > 0.)
let tryTBillPrice settlement maturity discount =
let dc = dayCount DayCountBasis.ActualActual
let dsm = dc.DaysBetween settlement maturity NumDenumPosition.Numerator
(100. * (1. - discount * dsm / 360.)) > 0. &&
(maturity > settlement) &&
(maturity <= (addYears settlement 1)) &&
(discount > 0.)
let tryDollarFr fractionalDollar fraction =
(fraction > 0.) &&
(pow 10. (ceiling (log10 (floor fraction))) <> 0.)
let tryDollarDe fractionalDollar fraction =
(fraction > 0.)
let tryEffect nominalRate npery =
(nominalRate > 0.) &&
(npery >= 1.)
let tryNominal effectRate npery =
(effectRate > 0.) &&
(npery >= 1.)
let tryDuration settlement maturity coupon yld frequency basis =
(maturity > settlement) &&
(coupon >= 0.) &&
(yld >= 0.)
let tryMDuration settlement maturity coupon yld frequency basis =
(maturity > settlement) &&
(coupon >= 0.) &&
(yld >= 0.)
let tryOddFPrice settlement maturity issue firstCoupon rate yld redemption (frequency:Frequency) basis =
let (Date(my, mm, md)) = maturity
let endMonth = lastDayOfMonth my mm md
let numMonths = int (12. / (float(int frequency)))
let numMonthsNeg = - numMonths
let mutable dateT = maturity
let mutable mat = maturity
dateT <- changeMonth mat numMonthsNeg basis endMonth
while dateT > firstCoupon do
mat <- dateT
dateT <- changeMonth mat numMonthsNeg basis endMonth
// This is not in the docs !!!!!
(dateT = firstCoupon) &&
(maturity > firstCoupon) &&
(firstCoupon > settlement) &&
(settlement > issue) &&
(rate >= 0.) &&
(yld >= 0.) &&
(redemption >= 0.)
let tryCalcOddFYield settlement maturity issue firstCoupon rate pr redemption (frequency:Frequency) basis =
(maturity > firstCoupon) &&
(firstCoupon > settlement) &&
(settlement > issue) &&
(rate >= 0.) &&
(pr >= 0.) &&
(redemption >= 0.)
let tryOddLPrice settlement maturity lastInterest rate yld redemption (frequency:Frequency) basis =
(maturity > settlement) &&
(settlement > lastInterest) &&
(rate >= 0.) &&
(yld >= 0.) &&
(redemption >= 0.)
let tryOddLYield settlement maturity lastInterest rate pr redemption (frequency:Frequency) basis =
(maturity > settlement) &&
(settlement > lastInterest) &&
(rate >= 0.) &&
(pr >= 0.) &&
(redemption >= 0.)
let tryAmorLinc cost datePurchased firstPeriod salvage period rate basis =
( cost >= 0. ) &&
( salvage >= 0. ) &&
( salvage < cost ) &&
( period >= 0. ) &&
( rate >= 0. ) &&
(datePurchased < firstPeriod) &&
(basis <> DayCountBasis.Actual360 )
let tryAmorDegrc cost datePurchased firstPeriod salvage period rate basis =
let assetLife = 1. / rate
let between x1 x2 = assetLife >= x1 && assetLife <= x2
( not(between 0. 3.) ) &&
( not(between 4. 5.) ) &&
( cost >= 0. ) &&
( salvage >= 0. ) &&
( salvage < cost ) &&
( period >= 0. ) &&
( rate >= 0. ) &&
(datePurchased < firstPeriod) &&
(basis <> DayCountBasis.Actual360 )
================================================
FILE: src/ExcelFinancialFunctions/tvm.fs
================================================
// Time value of money routines, note the extensive treatment of error condition to help the user with sensible error messages
#light
namespace Excel.FinancialFunctions
open System
open Excel.FinancialFunctions.Common
module internal Tvm =
// Main formulas
let fvFactor r nper = (1. + r) ** nper
let pvFactor r nper = 1. / fvFactor r nper
let annuityCertainPvFactor r nper (pd:PaymentDue) = if r = 0. then nper else (1. + r * float pd) * (1. - pvFactor r nper) / r
let annuityCertainFvFactor r nper (pd:PaymentDue) = annuityCertainPvFactor r nper pd * fvFactor r nper
let nperFactor r pmt v (pd:PaymentDue) = v * r + pmt * ( 1. + r * float pd )
let pv r nper pmt fv pd = - (fv * pvFactor r nper + pmt * annuityCertainPvFactor r nper pd)
let fv r nper pmt pv pd = - (pv * fvFactor r nper + pmt * annuityCertainFvFactor r nper pd)
let pmt r nper pv fv pd = - (pv + fv * pvFactor r nper) / annuityCertainPvFactor r nper pd
let nper r pmt pv fv (pd:PaymentDue) = ln ( nperFactor r pmt (-fv) pd / nperFactor r pmt pv pd ) / ln (r+1.)
// Preconditions and special cases
let calcPv r nper pmt fv pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is less than -1 and nper not an integer"
( pmt <> 0. || fv <> 0. ) |> elseThrow "pmt or fv need to be different from 0"
( r <> -1.) |> elseThrow "r cannot be -100%"
pv r nper pmt fv pd
let calcFv r nper pmt pv pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer"
( r <> -1. || (r = -1. && nper > 0.) ) |> elseThrow "r cannot be -100% when nper is <= 0"
if r = -1. && pd = PaymentDue.BeginningOfPeriod then - (pv * fvFactor r nper)
elif r = -1. && pd = PaymentDue.EndOfPeriod then - (pv * fvFactor r nper + pmt)
else fv r nper pmt pv pd
let calcPmt r nper pv fv pd =
( raisable r nper) |> elseThrow "r is not raisable to nper (r is negative and nper not an integer"
( r <> -1. || (r = -1. && nper > 0. && pd = PaymentDue.EndOfPeriod) ) |> elseThrow "r cannot be -100% when nper is <= 0"
( annuityCertainPvFactor r nper pd <> 0. ) |> elseThrow "1 * pd + 1 - (1 / (1 + r)^nper) / nper has to be <> 0"
if r = -1. then -fv
else pmt r nper pv fv pd
let calcNper r pmt pv fv pd =
if r = 0. && pmt <> 0. then
- (fv + pv) / pmt
else
nper r pmt pv fv pd
let calcRri nper pv fv =
( nper > 0. ) |> elseThrow "nper must be > 0"
if fv = pv then 0.
else
( pv <> 0. ) |> elseThrow "pv must be non-zero unless fv is zero"
( fv/pv >= 0. ) |> elseThrow "fv and pv must have same sign"
( pow (fv/pv) (1.0/nper) ) - 1.
let calcRate nper pmt pv fv pd guess =
let haveRightSigns x y z =
not( sign x = sign y && sign y = sign z) &&
not (sign x = sign y && z = 0.) &&
not (sign x = sign z && y = 0.) &&
not (sign y = sign z && x = 0.)
( pmt <> 0. || pv <> 0. ) |> elseThrow "pmt or pv need to be different from 0"
( nper > 0.) |> elseThrow "nper needs to be more than 0"
( haveRightSigns pmt pv fv ) |> elseThrow "There must be at least a change in sign in pv, fv and pmt"
if fv = 0. && pv = 0. then
if pmt < 0. then -1. else 1.
else
let f = fun r -> calcFv r nper pmt pv pd - fv
findRoot f guess
let calcFvSchedule (pv:float) interests =
let mutable result = pv
for i in interests do result <- result * (1. + i)
result
let calcPduration rate pv fv =
( rate > 0. ) |> elseThrow "rate must be positive"
( pv > 0. ) |> elseThrow "pv must be positive"
( fv > 0. ) |> elseThrow "fv must be positive"
( (log fv) - (log pv) ) / log ( 1. + rate)
================================================
FILE: src/ExcelFinancialFunctions/wrapperdotnettype.fs
================================================
// I cannot use F# optional parameters because they end up forcing you to include the F# dll
// Instead I use overloading with parsimony to achieve similar goals
#light
namespace Excel.FinancialFunctions
open System
open System.Collections
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
open Excel.FinancialFunctions.Loan
open Excel.FinancialFunctions.Irr
open Excel.FinancialFunctions.Depreciation
open Excel.FinancialFunctions.DayCount
open Excel.FinancialFunctions.Bonds
open Excel.FinancialFunctions.TBill
open Excel.FinancialFunctions.Misc
open Excel.FinancialFunctions.OddBonds
/// A wrapper class to expose the Excel financial functions API to .NET clients
type Financial =
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/accrint-function-fe45d089-6722-4fb3-9379-e1f911d8dc74">ACCRINT function</a>
/// The accrued interest for a security that pays periodic interest
static member AccrInt (issue, firstInterest, settlement, rate, par, frequency, basis, calcMethod) =
calcAccrInt issue firstInterest settlement rate par frequency basis calcMethod
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/accrint-function-fe45d089-6722-4fb3-9379-e1f911d8dc74">ACCRINT function</a>
/// The accrued interest for a security that pays periodic interest, using "FromIssueToSettlement" calculation method
static member AccrInt (issue, firstInterest, settlement, rate, par, frequency, basis) =
calcAccrInt issue firstInterest settlement rate par frequency basis AccrIntCalcMethod.FromIssueToSettlement
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/accrintm-function-f62f01f9-5754-4cc4-805b-0e70199328a7">ACCRINTM function</a>
/// The accrued interest for a security that pays interest at maturity
static member AccrIntM (issue, settlement, rate, par, basis) =
calcAccrIntM issue settlement rate par basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/amordegrc-function-a14d0ca1-64a4-42eb-9b3d-b0dededf9e51">AMORDEGRC function</a>
/// The depreciation for each accounting period by using a depreciation coefficient
/// ExcelCompliant is used because Excel stores 13 digits. AmorDegrc algorithm rounds numbers
/// and returns different results unless the numbers get rounded to 13 digits before rounding them.
/// I.E. 22.49999999999999 is considered 22.5 by Excel, but 22.4 by the .NET framework
static member AmorDegrc (cost, datePurchased, firstPeriod, salvage, period, rate, basis, excelCompliant) =
calcAmorDegrc cost datePurchased firstPeriod salvage period rate basis excelCompliant
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/excel-functions-by-category-5f91f4e9-7b42-46d2-9bd1-63f26a86c0eb">AMORLINC function</a>
/// The depreciation for each accounting period
static member AmorLinc (cost, datePurchased, firstPeriod, salvage, period, rate, basis) =
calcAmorLinc cost datePurchased firstPeriod salvage period rate basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/coupdaybs-function-eb9a8dfb-2fb2-4c61-8e5d-690b320cf872">COUPDAYBS function</a>
/// The number of days from the beginning of the coupon period to the settlement date
static member CoupDaysBS (settlement, maturity, frequency, basis) =
calcCoupDaysBS settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/coupdays-function-cc64380b-315b-4e7b-950c-b30b0a76f671">COUPDAYS function</a>
/// The number of days in the coupon period that contains the settlement date
/// The Excel algorithm seems wrong in that it doesn't respect `coupDays = coupDaysBS + coupDaysNC`
/// This equality should stand. The differs from Excel by +/- one or two days when the date spans a leap year.
static member CoupDays (settlement, maturity, frequency, basis) =
calcCoupDays settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/coupdaysnc-function-5ab3f0b2-029f-4a8b-bb65-47d525eea547">COUPDAYSNC function</a>
/// The number of days from the settlement date to the next coupon date
static member CoupDaysNC (settlement, maturity, frequency, basis) =
calcCoupDaysNC settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/coupncd-function-fd962fef-506b-4d9d-8590-16df5393691f">COUPNCD function</a>
/// The next coupon date after the settlement date
static member CoupNCD (settlement, maturity, frequency, basis) =
calcCoupNCD settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/coupnum-function-a90af57b-de53-4969-9c99-dd6139db2522">COUPNUM function</a>
/// The number of coupons payable between the settlement date and maturity date
static member CoupNum (settlement, maturity, frequency, basis) =
calcCoupNum settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/couppcd-function-2eb50473-6ee9-4052-a206-77a9a385d5b3">COUPCD function</a>
/// The previous coupon date before the settlement date
static member CoupPCD (settlement, maturity, frequency, basis) =
calcCoupPCD settlement maturity frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/cumipmt-function-61067bb0-9016-427d-b95b-1a752af0e606">CUMIPMT function</a>
/// The cumulative interest paid between two periods
static member CumIPmt (rate, nper, pv, startPeriod, endPeriod, typ) =
calcCumipmt rate nper pv startPeriod endPeriod typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/cumprinc-function-94a4516d-bd65-41a1-bc16-053a6af4c04d">CUMPRINC function</a>
/// The cumulative principal paid on a loan between two periods
static member CumPrinc (rate, nper, pv, startPeriod, endPeriod, typ) =
calcCumprinc rate nper pv startPeriod endPeriod typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/db-function-354e7d28-5f93-4ff1-8a52-eb4ee549d9d7">DB function</a>
/// The depreciation of an asset for a specified period by using the fixed-declining balance method
static member Db (cost, salvage, life, period, month) =
calcDb cost salvage life period month
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/db-function-354e7d28-5f93-4ff1-8a52-eb4ee549d9d7">DB function</a>
/// The depreciation of an asset for a specified period by using the fixed-declining balance method
static member Db (cost, salvage, life, period) =
calcDb cost salvage life period 12.
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/ddb-function-519a7a37-8772-4c96-85c0-ed2c209717a5">DDB function</a>
/// The depreciation of an asset for a specified period by using the double-declining balance method or some other method that you specify
///
/// Excel Ddb has two interesting characteristics:
/// 1. It special cases ddb for fractional periods between 0 and 1 by considering them to be 1
/// 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.
/// This function makes the same 1. adjustment.
static member Ddb (cost, salvage, life, period, factor) =
calcDdb cost salvage life period factor
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/ddb-function-519a7a37-8772-4c96-85c0-ed2c209717a5">DDB function</a>
/// The depreciation of an asset for a specified period by using the double-declining balance method or some other method that you specify
static member Ddb (cost, salvage, life, period) =
calcDdb cost salvage life period 2.
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/disc-function-71fce9f3-3f05-4acf-a5a3-eac6ef4daa53">DISC function</a>
/// The discount rate for a security
static member Disc (settlement, maturity, pr, redemption, basis) =
calcDisc settlement maturity pr redemption basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/dollarde-function-db85aab0-1677-428a-9dfd-a38476693427">DOLLARDE function</a>
/// Converts a dollar price, expressed as a fraction, into a dollar price, expressed as a decimal number
static member DollarDe (fractionalDollar, fraction) =
calcDollarDe fractionalDollar fraction
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/dollarfr-function-0835d163-3023-4a33-9824-3042c5d4f495">DOLLARFR function</a>
/// Converts a dollar price, expressed as a decimal number, into a dollar price, expressed as a fraction
static member DollarFr (decimalDollar, fraction) =
calcDollarFr decimalDollar fraction
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/duration-function-b254ea57-eadc-4602-a86a-c8e369334038">DURATION function</a>
/// The annual duration of a security with periodic interest payments
static member Duration (settlement, maturity, coupon, yld, frequency, basis) =
calcDuration settlement maturity coupon yld frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/effect-function-910d4e4c-79e2-4009-95e6-507e04f11bc4">EFFECT function</a>
/// The effective annual interest rate
static member Effect (nominalRate, npery) =
calcEffect nominalRate npery
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/fv-function-2eef9f44-a084-4c61-bdd8-4fe4bb1b71b3">FV function</a>
/// The future value of an investment
static member Fv (rate, nper, pmt, pv, typ) =
calcFv rate nper pmt pv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/fvschedule-function-bec29522-bd87-4082-bab9-a241f3fb251d">FVSCHEDULE function</a>
/// The future value of an initial principal after applying a series of compound interest rates
static member FvSchedule (principal, schedule) =
calcFvSchedule principal schedule
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/intrate-function-5cb34dde-a221-4cb6-b3eb-0b9e55e1316f">INTRATE function</a>
/// The interest rate for a fully invested security
static member IntRate (settlement, maturity, investment, redemption, basis) =
calcIntRate settlement maturity investment redemption basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/ipmt-function-5cce0ad6-8402-4a41-8d29-61a0b054cb6f">IPMT function</a>
/// The interest payment for an investment for a given period
static member IPmt (rate, per, nper, pv, fv, typ) =
calcIpmt rate per nper pv fv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/irr-function-64925eaa-9988-495b-b290-3ad0c163c1bc">IRR function</a>
/// The internal rate of return for a series of cash flows
static member Irr (values, guess) =
calcIrr values guess
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/irr-function-64925eaa-9988-495b-b290-3ad0c163c1bc">IRR function</a>
/// The internal rate of return for a series of cash flows
static member Irr (values) =
calcIrr values 0.1
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/ispmt-function-fa58adb6-9d39-4ce0-8f43-75399cea56cc">ISPMT function</a>
/// Calculates the interest paid during a specific period of an investment
static member ISPmt (rate, per, nper, pv) =
calcIspmt rate per nper pv
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/mduration-function-b3786a69-4f20-469a-94ad-33e5b90a763c">MDURATION function</a>
/// The Macauley modified duration for a security with an assumed par value of $100
static member MDuration (settlement, maturity, coupon, yld, frequency, basis) =
calcMDuration settlement maturity coupon yld frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/mirr-function-b020f038-7492-4fb4-93c1-35c345b53524">MIRR function</a>
/// The internal rate of return where positive and negative cash flows are financed at different rates
static member Mirr (values, financeRate, reinvestRate) =
calcMirr values financeRate reinvestRate
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/nominal-function-7f1ae29b-6b92-435e-b950-ad8b190ddd2b">NOMINAL function</a>
/// The annual nominal interest rate
static member Nominal (effectRate, npery) =
calcNominal effectRate npery
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/nper-function-240535b5-6653-4d2d-bfcf-b6a38151d815">NPER function</a>
/// The number of periods for an investment
static member NPer (rate, pmt, pv, fv, typ) =
calcNper rate pmt pv fv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/npv-function-8672cb67-2576-4d07-b67b-ac28acf2a568">NPV function</a>
/// The net present value of an investment based on a series of periodic cash flows and a discount rate
static member Npv (rate, values) =
calcNpv rate values
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/oddfprice-function-d7d664a8-34df-4233-8d2b-922bcf6a69e1">ODDFPRICE function</a>
/// The price per $100 face value of a security with an odd first period
static member OddFPrice (settlement, maturity, issue, firstCoupon, rate, yld, redemption, frequency, basis) =
calcOddFPrice settlement maturity issue firstCoupon rate yld redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/oddfyield-function-66bc8b7b-6501-4c93-9ce3-2fd16220fe37">ODDFYIELD function</a>
/// The yield of a security with an odd first period
static member OddFYield (settlement, maturity, issue, firstCoupon, rate, pr, redemption, frequency, basis) =
calcOddFYield settlement maturity issue firstCoupon rate pr redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/oddlprice-function-fb657749-d200-4902-afaf-ed5445027fc4">ODDLPRICE function</a>
/// The price per $100 face value of a security with an odd last period
static member OddLPrice (settlement, maturity, lastInterest, rate, yld, redemption, frequency, basis) =
calcOddLPrice settlement maturity lastInterest rate yld redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/oddlyield-function-c873d088-cf40-435f-8d41-c8232fee9238">ODDLYIELD function</a>
/// The yield of a security with an odd last period
static member OddLYield (settlement, maturity, lastInterest, rate, pr, redemption, frequency, basis) =
calcOddLYield settlement maturity lastInterest rate pr redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/pmt-function-0214da64-9a63-4996-bc20-214433fa6441">PMT function</a>
/// The periodic payment for an annuity
static member Pmt (rate, nper, pv, fv, typ) =
calcPmt rate nper pv fv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/ppmt-function-c370d9e3-7749-4ca4-beea-b06c6ac95e1b">PPMT function</a>
/// The payment on the principal for an investment for a given period
static member PPmt (rate, per, nper, pv, fv, typ) =
calcPpmt rate per nper pv fv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/price-function-3ea9deac-8dfa-436f-a7c8-17ea02c21b0a">PRICE function</a>
/// The price per $100 face value of a security that pays periodic interest
static member Price (settlement, maturity, rate, yld, redemption, frequency, basis) =
calcPrice settlement maturity rate yld redemption frequency basis
/// The price per $100 face value of a security that pays periodic interest
/// This is the same calculation as "Price", but allows a negative yield. Excel does not allow negative yield, so this is an addition to the Excel-
/// compatible UI
static member PriceAllowNegativeYield (settlement, maturity, rate, yld, redemption, frequency, basis) =
calcPriceAllowNegativeYield settlement maturity rate yld redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/pricedisc-function-d06ad7c1-380e-4be7-9fd9-75e3079acfd3">PRICEDISC function</a>
/// The price per $100 face value of a discounted security
static member PriceDisc (settlement, maturity, discount, redemption, basis) =
calcPriceDisc settlement maturity discount redemption basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/pricemat-function-52c3b4da-bc7e-476a-989f-a95f675cae77">PRICEMAT function</a>
/// The price per $100 face value of a security that pays interest at maturity
static member PriceMat (settlement, maturity, issue, rate, yld, basis) =
calcPriceMat settlement maturity issue rate yld basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/pv-function-23879d31-0e02-4321-be01-da16e8168cbd">PV function</a>
/// The present value of an investment
static member Pv (rate, nper, pmt, fv, typ) =
calcPv rate nper pmt fv typ
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/rate-function-9f665657-4a7e-4bb7-a030-83fc59e748ce">RATE function</a>
/// The interest rate per period of an annuity
static member Rate (nper, pmt, pv, fv, typ, guess) =
calcRate nper pmt pv fv typ guess
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/rate-function-9f665657-4a7e-4bb7-a030-83fc59e748ce">RATE function</a>
/// The interest rate per period of an annuity
static member Rate (nper, pmt, pv, fv, typ) =
calcRate nper pmt pv fv typ 0.1
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/received-function-7a3f8b93-6611-4f81-8576-828312c9b5e5">RECEIVED function</a>
/// The amount received at maturity for a fully invested security
static member Received (settlement, maturity, investment, discount,basis) =
calcReceived settlement maturity investment discount basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/sln-function-cdb666e5-c1c6-40a7-806a-e695edc2f1c8">SLN function</a>
/// The straight-line depreciation of an asset for one period
static member Sln (cost, salvage, life) =
calcSln cost salvage life
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/syd-function-069f8106-b60b-4ca2-98e0-2a0f206bdb27">SYD function</a>
/// The sum-of-years' digits depreciation of an asset for a specified period
static member Syd (cost, salvage, life, per) =
calcSyd cost salvage life per
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/tbilleq-function-2ab72d90-9b4d-4efe-9fc2-0f81f2c19c8c">TBILLEQ function</a>
/// The bond-equivalent yield for a Treasury bill
static member TBillEq (settlement, maturity, discount) =
calcTBillEq settlement maturity discount
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/tbillprice-function-eacca992-c29d-425a-9eb8-0513fe6035a2">TBILLPRICE function</a>
/// The price per $100 face value for a Treasury bill
static member TBillPrice (settlement, maturity, discount) =
calcTBillPrice settlement maturity discount
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/tbillyield-function-6d381232-f4b0-4cd5-8e97-45b9c03468ba">TBILLYIELD function</a>
/// The yield for a Treasury bill
static member TBillYield (settlement, maturity, pr) =
calcTBillYield settlement maturity pr
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/vdb-function-dde4e207-f3fa-488d-91d2-66d55e861d73">VDB function</a>
/// The depreciation of an asset for a specified or partial period by using a declining balance method.
///
/// 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)
/// `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)`
/// Notice that in Excel by using '1' (no_switch) instead of '0' as the last parameter everything works as expected.
/// 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.
/// Overall, I think my algorithm is correct, even if it disagrees with Excel when startperiod is fractional.
static member Vdb (cost, salvage, life, startPeriod, endPeriod, factor, noSwitch) =
calcVdb cost salvage life startPeriod endPeriod factor noSwitch
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/vdb-function-dde4e207-f3fa-488d-91d2-66d55e861d73">VDB function</a>
/// The depreciation of an asset for a specified or partial period by using a declining balance method
static member Vdb (cost, salvage, life, startPeriod, endPeriod, factor) =
calcVdb cost salvage life startPeriod endPeriod factor VdbSwitch.SwitchToStraightLine
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/vdb-function-dde4e207-f3fa-488d-91d2-66d55e861d73">VDB function</a>
/// The depreciation of an asset for a specified or partial period by using a declining balance method
static member Vdb (cost, salvage, life, startPeriod, endPeriod) =
calcVdb cost salvage life startPeriod endPeriod 2. VdbSwitch.SwitchToStraightLine
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d">XIRR function</a>
/// The internal rate of return for a schedule of cash flows that is not necessarily periodic
static member XIrr (values, dates, guess) =
calcXirr values dates guess
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/xirr-function-de1242ec-6477-445b-b11b-a303ad9adc9d">XIRR function</a>
/// The internal rate of return for a schedule of cash flows that is not necessarily periodic
static member XIrr (values, dates) =
calcXirr values dates 0.1
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/xnpv-function-1b42bbf6-370f-4532-a0eb-d67c16b664b7">XNPV function</a>
/// The net present value for a schedule of cash flows that is not necessarily periodic
static member XNpv (rate, values, dates) =
calcXnpv rate values dates
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/yield-function-f5f5ca43-c4bd-434f-8bd2-ed3c9727a4fe">YIELD function</a>
/// The yield on a security that pays periodic interest
static member Yield (settlement, maturity, rate, pr, redemption, frequency, basis) =
calcYield settlement maturity rate pr redemption frequency basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/yielddisc-function-a9dbdbae-7dae-46de-b995-615faffaaed7">YIELDDISC function</a>
/// The annual yield for a discounted security; for example, a Treasury bill
static member YieldDisc (settlement, maturity, pr, redemption, basis) =
calcYieldDisc settlement maturity pr redemption basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/yieldmat-function-ba7d1809-0d33-4bcb-96c7-6c56ec62ef6f">YIELDMAT function</a>
/// The annual yield of a security that pays interest at maturity
static member YieldMat (settlement, maturity, issue, rate, pr, basis) =
calcYieldMat settlement maturity issue rate pr basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/yearfrac-function-3844141e-c76d-4143-82b6-208454ddc6a8">YEARFRAC function</a>
/// Calculates the fraction of the year represented by the number of whole days between two dates - not a financial function
static member YearFrac (startDate, endDate, basis) =
calcYearFrac startDate endDate basis
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/rri-function-6f5822d8-7ef1-4233-944c-79e8172930f4">RRI function</a>
/// Returns an equivalent interest rate for the growth of an investment
static member Rri (nper, pv, fv) =
calcRri nper pv fv
/// <a target="_blank" href="https://support.microsoft.com/en-us/office/pduration-function-44f33460-5be5-4c90-b857-22308892adaf">PDURATION function</a>
/// Returns the number of periods required by an investment to reach a specified value.
static member Pduration (rate, pv, fv) =
calcPduration rate pv fv
================================================
FILE: tests/ExcelFinancialFunctions.ConsoleTests/ExcelFinancialFunctions.ConsoleTests.fsproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarnOn>3390;$(WarnOn)</WarnOn>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" />
<Compile Include="excel.fs" />
<Compile Include="testinfrastructure.fs" />
<Compile Include="testsdef.fs" />
<Compile Include="MatrixTests.fs" />
<Compile Include="SpotTests.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Office.Interop.Excel" Version="15.0.4795.1000" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.1.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\ExcelFinancialFunctions\ExcelFinancialFunctions.fsproj" />
</ItemGroup>
</Project>
================================================
FILE: tests/ExcelFinancialFunctions.ConsoleTests/MatrixTests.fs
================================================
namespace Excel.FinancialFunctions
open NUnit.Framework
open TestInfrastructure
open Excel.FinancialFunctions.ExcelTesting
open Excel.FinancialFunctions.Tvm
open Excel.FinancialFunctions.Loan
open Excel.FinancialFunctions.Irr
open Excel.FinancialFunctions.Depreciation
open Excel.FinancialFunctions.DayCount
open Excel.FinancialFunctions.TestsDef
open Excel.FinancialFunctions.Bonds
open Excel.FinancialFunctions.TBill
open Excel.FinancialFunctions.Misc
open Excel.FinancialFunctions.OddBonds
open Excel.FinancialFunctions.TestPreconditions
[<Parallelizable(ParallelScope.Children)>]
type MatrixTests () =
let tests = [|
// --------------------------------------------------------------------------------------------------
//"RATE", Excel uses a different root finding algo. Sometimes mine is better, sometimes Excel's is. Using the TestRate function instead.
//"ODDFYIELD", Excel uses a different root finding algo. Sometimes mine is better, sometimes Excel's is. Using the TestOddFYield function instead.
//"XNPV", the excel object model has an xnpv function with a different number of args. Tested through XIRR and separate testcase.
//"YIELD", the excel object model lacks this function. Tested through the price function and separate testcase.
// --------------------------------------------------------------------------------------------------
"PV", (fun _ -> test5 calcPv pvEx rates npers pmts fvs dues tryPv);
"FV", (fun _ -> test5 calcFv fvEx rates npers pmts pvs dues tryFv);
"PMT", (fun _ -> test5 calcPmt pmtEx rates npers pvs fvs dues tryPmt);
"NPER", (fun _ -> test5 calcNper nperEx rates pmts pvs fvs dues tryNper);
"IPMT", (fun _ -> test6 calcIpmt ipmtEx rates pers npers pvs fvs dues tryIpmt);
"PPMT", (fun _ -> test6 calcPpmt ppmtEx rates pers npers pvs fvs dues tryPpmt);
"CUMIPMT", (fun _ -> test6 calcCumipmt cumipmtEx rates npers pvs pers endPers dues tryCumipmt);
"CUMPRINC", (fun _ -> test6 calcCumprinc cumprincEx rates npers pvs pers endPers dues tryCumprinc);
"ISPMT", (fun _ -> test4 calcIspmt ispmtEx rates pers npers pvs tryIspmt);
"FVSCHEDULE", (fun _ -> test2 calcFvSchedule fvScheduleEx pvs testInterests precondOk2);
"IRR", (fun _ -> test2 calcIrr irrEx testCfs guesses precondOk2);
"NPV", (fun _ -> test2 calcNpv npvEx rates testCfs tryNpv);
"MIRR", (fun _ -> test3 calcMirr mirrEx testCfs rates rates tryMirr);
//"XIRR", fun _-> test3 calcXirr xirrEx testCfs testDates guesses tryXirr;
"DB", fun _ -> test5 calcDb dbEx testCosts testSalvages testLives testPeriods testMonths tryDb;
"SLN", fun _ -> test3 calcSln slnEx testCosts testSalvages testLives trySln;
"SYD", fun _ -> test4 calcSyd sydEx testCosts testSalvages testLives testPeriods trySyd;
"DDB", fun _ -> test5 calcDdb ddbEx testCosts testSalvages testLives testDdbPeriods testFactors tryDdb;
"VDB excluding fractional startdates", fun _ -> test7 vdbWrap vdbEx testCosts testSalvages testLives testPeriods testEndPeriods testFactors testVdbSwitch tryVdb;
"AMORLINC", fun _ -> test7 calcAmorLinc amorLincEx testCosts testIssueDates testFirstInterestDates testSalvages testPeriods testBondRates testDayCountBasis tryAmorLinc;
"AMORDEGRC", fun _ -> test7 amorDegrcWrapper amorDegrcEx testCosts testIssueDates testFirstInterestDates testSalvages testPeriods testDeprRates testDayCountBasis tryAmorDegrc;
"COUPDAYS excluding leap years", fun _ -> test4 calcCoupDays coupDaysEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDays;
"COUPDAYSBS", fun _ -> test4 calcCoupDaysBS coupDaysBSEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDaysBS;
"COUPDAYSNC", fun _ -> test4 calcCoupDaysNC coupDaysNCEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupDaysNC;
"COUPNUM", fun _ -> test4 calcCoupNum coupNumEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupNum;
"COUPPCD", fun _ -> test4 coupPCDWrapper coupPCDEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupPCD;
"COUPNCD", fun _ -> test4 coupNCDWrapper coupNCDEx testSettlDates testMatDates testFrequency testDayCountBasis tryCoupNCD;
"ACCRINTM", fun _ -> test5 calcAccrIntM accrIntMEx testIssue testSettl testBondRates testPars testDayCountBasis tryAccrIntM;
"ACCRINT", fun _ -> test7 calcAccrIntWrap accrIntEx testIssue testFirstInt testSettl testBondRates testPars testFrequency testDayCountBasis tryAccrInt;
"PRICE", fun _ -> test7 calcPrice priceEx testSettlDates testMatDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryPrice
"PRICEMAT", fun _ -> test6 calcPriceMat priceMatEx testSettlDates testMatDates testIssue testBondRates testYlds testDayCountBasis tryPriceMat
"YIELDMAT", fun _ -> test6 calcYieldMat yieldMatEx testSettlDates testMatDates testIssue testBondRates testPrices testDayCountBasis tryYieldMat
"YEARFRAC", fun _ -> test3 calcYearFrac yearFracEx testSDates testEDates testDayCountBasis tryYearFrac;
"INTRATE", fun _ -> test5 calcIntRate intRateEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryIntRate;
"RECEIVED", fun _ -> test5 calcReceived receivedEx testSettlDates testMatDates testInvestments testDiscounts testDayCountBasis tryReceived;
"DISC", fun _ -> test5 calcDisc discEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryDisc;
"PRICEDISC", fun _ -> test5 calcPriceDisc priceDiscEx testSettlDates testMatDates testDiscounts testRedemptions testDayCountBasis tryPriceDisc;
"YIELDDISC", fun _ -> test5 calcYieldDisc yieldDiscEx testSettlDates testMatDates testInvestments testRedemptions testDayCountBasis tryYieldDisc;
"TBILLEQ", fun _ -> test3 calcTBillEq TBillEqEx testSettlDates testTBillMat testDiscounts tryTBillEq;
"TBILLYIELD", fun _ -> test3 calcTBillYield TBillYieldEx testSettlDates testTBillMat testPrices tryTBillYield;
"TBILLPrice", fun _ -> test3 calcTBillPrice TBillPriceEx testSettlDates testTBillMat testDiscounts tryTBillPrice;
"DOLLARDE", fun _ -> test2 calcDollarDe dollarDeEx testFractionalDollars testFractions tryDollarDe;
"DOLLARFR", fun _ -> test2 calcDollarFr dollarFrEx testFractionalDollars testFractions tryDollarFr;
"EFFECT", fun _ -> test2 calcEffect effectEx rates testPeriods tryEffect;
"NOMINAL", fun _ -> test2 calcNominal nominalEx rates testPeriods tryNominal;
"DURATION", fun _ -> test6 calcDuration durationEx testSettlDates testMatDates testInvestments testYlds testFrequency testDayCountBasis tryDuration;
"MDURATION", fun _ -> test6 calcMDuration mdurationEx testSettlDates testMatDates testInvestments testYlds testFrequency testDayCountBasis tryMDuration;
"ODDFPRICE", fun _ -> test9 calcOddFPrice oddFPriceEx testSettlDates2 testMatDates testIssueDates testFirstInterestDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryOddFPrice;
"ODDLPRICE", fun _ -> test8 calcOddLPrice oddLPriceEx testSettlDates2 testMatDates testIssueDates testBondRates testYlds testRedemptions testFrequency testDayCountBasis tryOddLPrice;
"ODDLYIELD", fun _ -> test8 calcOddLYield oddLYieldEx testSettlDates2 testMatDates testIssueDates testBondRates testRedemptions testRedemptions testFrequency testDayCountBasis tryOddLYield;
|]
[<TestCase("PV")>]
[<TestCase("FV")>]
[<TestCase("PMT")>]
[<TestCase("NPER")>]
[<TestCase("IPMT")>]
[<TestCase("PPMT")>]
[<TestCase("CUMIPMT",Category="Fast")>]
[<TestCase("CUMPRINC",Category="Fast")>]
[<TestCase("ISPMT")>]
[<TestCase("DB")>]
[<TestCase("SLN",Category="Fast")>]
[<TestCase("SYD",Category="Fast")>]
[<TestCase("DDB")>]
[<TestCase("VDB excluding fractional startdates")>]
[<TestCase("FVSCHEDULE",Category="Fast")>]
[<TestCase("MIRR",Category="Fast")>]
[<TestCase("NPV")>]
[<TestCase("ISPMT")>]
[<TestCase("IRR",Category="Fast")>]
[<TestCase("AMORLINC")>]
[<TestCase("AMORDEGRC")>]
[<TestCase("COUPDAYS excluding leap years")>]
[<TestCase("COUPDAYSBS")>]
[<TestCase("COUPDAYSNC")>]
[<TestCase("COUPNUM")>]
[<TestCase("COUPPCD")>]
[<TestCase("COUPNCD")>]
[<TestCase("ACCRINTM")>]
[<TestCase("ACCRINT")>]
[<TestCase("PRICE")>]
[<TestCase("PRICEMAT")>]
[<TestCase("YIELDMAT")>]
[<TestCase("YEARFRAC")>]
[<TestCase("INTRATE")>]
[<TestCase("RECEIVED")>]
[<TestCase("DISC")>]
[<TestCase("PRICEDISC")>]
[<TestCase("YIELDDISC")>]
[<TestCase("TBILLEQ")>]
[<TestCase("TBILLYIELD",Category="Fast")>]
[<TestCase("TBILLPrice",Category="Fast")>]
[<TestCase("DOLLARDE",Category="Fast")>]
[<TestCase("DOLLARFR",Category="Fast")>]
[<TestCase("EFFECT",Category="Fast")>]
[<TestCase("NOMINAL",Category="Fast")>]
[<TestCase("DURATION")>]
[<TestCase("MDURATION")>]
[<TestCase("ODDFPRICE")>]
[<TestCase("ODDLPRICE")>]
[<TestCase("ODDLYIELD")>]
member __.RunMatrix test =
let found = Array.tryFind (fun x -> fst x = test) tests
let (_,func) = found.Value
let (tries,success,_,_) = func ()
Assert.AreEqual(success,tries)
================================================
FILE: tests/ExcelFinancialFunctions.ConsoleTests/README.md
================================================
# Console Tests
These tests require Excel 2013 or later installed on your host machine. They directly
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
them might be "Interop Tests".
They also take a very long time to run, as the test matrices get quite complex. For example on my
12-core machine, they run for 15 minutes.
You can run just the "Fast" tests as a smoke test:
```
PS tests\ExcelFinancialFunctions.ConsoleTests> dotnet build
Microsoft (R) Build Engine version 16.11.0+0538acc04 for .NET
Copyright (C) Microsoft Corporation. All rights reserved.
ExcelFinancialFunctions -> \src\ExcelFinancialFunctions\bin\Debug\netstandard2.0\ExcelFinancialFunctions.dll
ExcelFinancialFunctions.ConsoleTests -> \tests\ExcelFinancialFunctions.ConsoleTests\bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.75
PS \tests\ExcelFinancialFunctions.ConsoleTests> vstest.console.exe bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll --TestCaseFilter:"Category=Fast"
Microsoft (R) Test Execution Command Line Tool Version 16.11.0
Copyright (c) Microsoft Corporation. All rights reserved.
A total of 1 test files matched the specified pattern.
NUnit Adapter 4.1.0.0: Test execution started
Running selected tests in \tests\ExcelFinancialFunctions.ConsoleTests\bin\Debug\net48\ExcelFinancialFunctions.ConsoleTests.dll
NUnit3TestExecutor discovered 69 of 69 NUnit test cases using Current Discovery mode, Non-Explicit run
Passed RunMatrix("IRR") [912 ms]
...
Test Run Successful.
Total tests: 69
Passed: 69
Total time: 5.2782 Seconds
```
================================================
FILE: tests/ExcelFinancialFunctions.ConsoleTests/SpotTests.fs
================================================
namespace Excel.FinancialFunctions
open System
open NUnit.Framework
open TestInfrastructure
open Excel.FinancialFunctions
open Excel.FinancialFunctions.ExcelTesting
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
open Excel.FinancialFunctions.Loan
open Excel.FinancialFunctions.Irr
open Excel.FinancialFunctions.Depreciation
open Excel.FinancialFunctions.DayCount
open Excel.FinancialFunctions.TestInfrastructure
open Excel.FinancialFunctions.TestsDef
open Excel.FinancialFunctions.Bonds
open Excel.FinancialFunctions.TBill
open Excel.FinancialFunctions.Misc
open Excel.FinancialFunctions.OddBonds
open Excel.FinancialFunctions.TestPreconditions
[<Category("Fast")>]
[<Parallelizable(ParallelScope.Children)>]
type SpotTests () =
let spotTest1 f1 f2 p1 =
Assert.AreEqual(f1 p1,f2 p1,precision)
let spotTest2 f1 f2 p1 p2 =
// printfn "%A %A %A" p1 p2 (f1 p1 p2)
Assert.AreEqual(f1 p1 p2,f2 p1 p2,precision)
let spotTest3 f1 f2 p1 p2 p3 =
// printfn "%A %A %A %A" p1 p2 p3 (f1 p1 p2 p3)
Assert.AreEqual(f1 p1 p2 p3,f2 p1 p2 p3,precision)
let spotTest4 f1 f2 p1 p2 p3 p4 =
// printfn "%A %A %A %A %A" p1 p2 p3 p4 (f1 p1 p2 p3 p4)
Assert.AreEqual(f1 p1 p2 p3 p4,f2 p1 p2 p3 p4,precision)
let spotTest5 f1 f2 p1 p2 p3 p4 p5 =
Assert.AreEqual(f1 p1 p2 p3 p4 p5,f2 p1 p2 p3 p4 p5,precision)
let spotTest6 f1 f2 p1 p2 p3 p4 p5 p6 =
Assert.AreEqual(f1 p1 p2 p3 p4 p5 p6,f2 p1 p2 p3 p4 p5 p6,precision)
let spotTest7 f1 f2 p1 p2 p3 p4 p5 p6 p7 =
Assert.AreEqual(f1 p1 p2 p3 p4 p5 p6 p7,f2 p1 p2 p3 p4 p5 p6 p7,precision)
let spotTest8 f1 f2 p1 p2 p3 p4 p5 p6 p7 p8 =
Assert.AreEqual(f1 p1 p2 p3 p4 p5 p6 p7 p8,f2 p1 p2 p3 p4 p5 p6 p7 p8,precision)
let spotTest9 f1 f2 p1 p2 p3 p4 p5 p6 p7 p8 p9 =
Assert.AreEqual(f1 p1 p2 p3 p4 p5 p6 p7 p8 p9,f2 p1 p2 p3 p4 p5 p6 p7 p8 p9,precision)
[<Test>]
member __.calcPv() =
spotTest5 calcPv pvEx 0.3 10. 20. 100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcFv() =
spotTest5 calcFv fvEx 0.3 10. 20. 100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcPmt() =
spotTest5 calcPmt pmtEx 0.3 10. -20. 100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcIpmt() =
spotTest6 calcIpmt ipmtEx 0.3 3. 10. -20. 100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcPpmt() =
spotTest6 calcPpmt ppmtEx 0.3 4. 10. -20. 100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcCumipmt() =
spotTest6 calcCumipmt cumipmtEx 0.2 10. 100. 2. 5. PaymentDue.EndOfPeriod
[<Test>]
member __.calcCumprinc() =
spotTest6 calcCumprinc cumprincEx 0.2 10. 100. 2. 5. PaymentDue.EndOfPeriod
[<Test>]
member __.calcNper() =
spotTest5 calcNper nperEx 0.3 10. 20. -100. PaymentDue.EndOfPeriod
[<Test>]
member __.calcFvSchedule() =
spotTest2 calcFvSchedule fvScheduleEx 100. [|0.13;0.14;-0.2;0.34;-0.12|]
[<Test>]
member __.calcIrr() =
spotTest2 calcIrr irrEx [|-123.; 12.; 15.; 50.; 200.|] 0.14
[<Test>]
member __.calcNpv() =
spotTest2 calcNpv npvEx 0.14 [|-123.; 12.; 15.; 50.; 200.|]
[<Test>]
member __.calcMirr() =
spotTest3 calcMirr mirrEx [|-123.; 12.; 15.; 50.; 200.|] 0.14 0.12
[<Test>]
member __.calcXirr() =
spotTest3 calcXirr xirrEx [|-1.;3.;4.|] [|date 1970 3 2; date 1988 2 3; date 1999 3 5|] 0.14
[<Test>]
member __.calcDb() =
spotTest5 calcDb dbEx 122. 12. 12. 2. 3.
[<Test>]
member __.calcSln() =
spotTest3 calcSln slnEx 122. 20. 12.
[<Test>]
member __.calcSyd() =
spotTest4 calcSyd sydEx 130. 10. 10. 4.
[<Test>]
member __.calcDdb() =
spotTest5 calcDdb ddbEx 120. 20. 10. 4. 3.
[<Test>]
member __.vdbWrap() =
spotTest7 vdbWrap vdbEx 100. 20. 20. 2. 3. 3. VdbSwitch.DontSwitchToStraightLine
[<Test>]
member __.calcIspmt() =
spotTest4 calcIspmt ispmtEx 0.15 3. 10. 100.
[<Test>]
member __.calcCoupDays() =
spotTest4 calcCoupDays coupDaysEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.coupPCDWrapper() =
spotTest4 coupPCDWrapper coupPCDEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.coupNCDWrapper() =
spotTest4 coupNCDWrapper coupNCDEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcCoupNum() =
spotTest4 calcCoupNum coupNumEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcCoupDaysBS() =
spotTest4 calcCoupDaysBS coupDaysBSEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcCoupDaysNC() =
spotTest4 calcCoupDaysNC coupDaysNCEx (date 1984 3 4) (date 1990 4 5) Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcAccrIntM() =
spotTest5 calcAccrIntM accrIntMEx (date 1984 3 4) (date 1991 4 5) 0.07 120. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcAccrIntWrap() =
spotTest7 calcAccrIntWrap accrIntEx (date 1984 3 4) (date 1994 3 4) (date 1991 4 5) 0.07 120. Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcPrice() =
spotTest7 calcPrice priceEx (date 1984 3 4) (date 1990 3 4) 0.07 0.1 110. Frequency.Quarterly DayCountBasis.ActualActual
[<Test>]
member __.calcPriceMat() =
spotTest6 calcPriceMat priceMatEx (date 2008 2 13) (date 2009 4 13) (date 2007 11 11) 0.061 0.061 DayCountBasis.UsPsa30_360
[<Test>]
member __.calcYieldMat() =
spotTest6 calcYieldMat yieldMatEx (date 2008 2 13) (date 2009 4 13) (date 2007 11 11) 0.061 120. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcYearFrac() =
spotTest3 calcYearFrac yearFracEx (date 2008 2 13) (date 2009 4 13) DayCountBasis.ActualActual
[<Test>]
member __.calcIntRate1() =
spotTest5 calcIntRate intRateEx (date 2008 2 13) (date 2010 4 13) 100. 150. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcIntRate2() =
spotTest5 calcIntRate intRateEx (date 2008 3 13) (date 2010 5 13) 100. 0.15 DayCountBasis.UsPsa30_360
[<Test>]
member __.calcDisc() =
spotTest5 calcDisc discEx (date 2008 2 13) (date 2011 5 13) 75. 100. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcPriceDisc() =
spotTest5 calcPriceDisc priceDiscEx (date 2008 2 13) (date 2013 5 13) 0.25 100. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcYieldDisc() =
spotTest5 calcYieldDisc yieldDiscEx (date 2008 2 28) (date 2011 5 13) 75. 100. DayCountBasis.UsPsa30_360
[<Test>]
member __.calcTBillEq() =
spotTest3 calcTBillEq TBillEqEx (date 2008 2 13) (date 2009 1 11) 0.25
[<Test>]
member __.calcTBillYield() =
spotTest3 calcTBillYield TBillYieldEx (date 2008 2 28) (date 2009 2 27) 0.25
[<Test>]
member __.calcTBillPrice() =
spotTest3 calcTBillPrice TBillPriceEx (date 2008 2 29) (date 2009 2 27) 0.25
[<Test>]
member __.calcDollarDe() =
spotTest2 calcDollarDe dollarDeEx 1.125 16.
[<Test>]
member __.calcDollarFr() =
spotTest2 calcDollarFr dollarFrEx 1.125 16.
[<Test>]
member __.calcEffect() =
spotTest2 calcEffect effectEx 0.0525 4.
[<Test>]
member __.calcNominal() =
spotTest2 calcNominal nominalEx 0.053543 4.
[<Test>]
member __.calcDuration() =
spotTest6 calcDuration durationEx (date 2008 2 13) (date 2011 5 13) 100. 0.07 Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcMDuration() =
spotTest6 calcMDuration mdurationEx (date 2008 2 13) (date 2011 5 13) 100. 0.07 Frequency.Quarterly DayCountBasis.UsPsa30_360
[<Test>]
member __.calcOddFPrice() =
spotTest9 calcOddFPrice oddFPriceEx (date 2008 11 11) (date 2021 3 1) (date 2008 10 15) (date 2009 3 1) 0.0785 0.0625 100. Frequency.SemiAnnual DayCountBasis.ActualActual
[<Test>]
member __.calcOddFYield() =
spotTest9 calcOddFYield oddFYieldEx (date 2008 11 11) (date 2021 3 1) (date 2008 10 15) (date 2009 3 1) 0.0575 84.5 100. Frequency.SemiAnnual DayCountBasis.UsPsa30_360
[<Test>]
member __.calcOddLPrice() =
spotTest8 calcOddLPrice oddLPriceEx (date 2008 2 7) (date 2008 6 15) (date 2007 10 15) 0.0375 0.0405 100. Frequency.SemiAnnual DayCountBasis.UsPsa30_360
[<Test>]
member __.calcOddLYield() =
spotTest8 calcOddLYield oddLYieldEx (date 2008 4 20) (date 2008 6 15) (date 2007 12 24) 0.0375 99.875 100. Frequency.SemiAnnual DayCountBasis.UsPsa30_360
[<Test>]
member __.calcAmorLinc() =
spotTest7 calcAmorLinc amorLincEx 2400. (date 2008 8 19) (date 2008 12 31) 300. 1. 0.15 DayCountBasis.ActualActual
[<Test>]
member __.amorDegrcWrapper() =
spotTest7 amorDegrcWrapper amorDegrcEx 2400. (date 2008 8 19) (date 2008 12 31) 300. 1. 0.15 DayCountBasis.ActualActual
// Need to test this manually because of the differences in root finding algos
[<Test>]
member __.testRate() =
spotTest6 calcRate rateEx 1. 10. 100. -100. PaymentDue.EndOfPeriod 0.15
spotTest6 calcRate rateEx 5. 20. 120. -50. PaymentDue.BeginningOfPeriod 0.
spotTest6 calcRate rateEx 10. -10. 0. 100. PaymentDue.EndOfPeriod -0.15
spotTest6 calcRate rateEx 25. -40. -200. 100. PaymentDue.BeginningOfPeriod 0.15
[<Test>]
member __.calcXnpv() =
let result = calcXnpv 0.14 [1.;3.;4.] [date 1970 3 2; date 1988 2 3; date 1999 3 5]
Assert.AreEqual(1.375214,result,precision)
[<Test>]
member __.calcYield() =
let result = calcYield (date 2008 2 15) (date 2016 11 15) 0.0575 95.04287 100. Frequency.SemiAnnual DayCountBasis.UsPsa30_360
Assert.AreEqual(0.065,result,precision)
[<Test>]
member __.testXirrBugs() =
let t = @"-185550.98 5/15/2008
-231887.53 5/19/2008
-26756.74 5/30/2008
-384010.86 6/20/2008
-27114.54 6/26/2008
-458667.97 8/21/2008
-217853.67 9/8/2008
-424924.25 10/13/2008
-75076.01 10/14/2008
-389630.32 10/24/2008
-112094.2 11/19/2008
-25646.4 11/21/2008
-24164.69 11/21/2008
-1222.08 11/21/2008
-556.91 12/3/2008
1204954.004 12/5/2008"
let pairs = t.Split([|' '; '\n'; '\r'|]) |> Array.filter (fun x -> not(x = ""))
let dates = pairs |> Array.filter (fun x -> fst(DateTime.TryParse(x))) |> Array.map (fun x -> DateTime.Parse(x))
let values = pairs |> Array.filter (fun x -> not(fst(DateTime.TryParse(x)))) |> Array.map (fun x -> float x)
let guess = - 0.1
spotTest3 calcXirr xirrEx values dates guess
let values = [|105091006.;-103250941.864729|]
let dates = [|date 2000 4 10; date 2000 4 30|]
spotTest3 calcXirr xirrEx values dates guess
let values = [|206101714.849377;-156650972.54265|]
let dates = [|date 2001 2 28; date 2001 3 31|]
spotTest3 calcXirr xirrEx values dates guess
let values = [|15108163.3840923;-75382259.6628424|]
let dates = [|date 2000 2 29; date 2000 3 31|]
let result = calcXirr values dates guess
Assert.AreEqual(165601346.13484925,result,precision)
[<Test>]
member __.testOddFYield () =
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
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
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
================================================
FILE: tests/ExcelFinancialFunctions.ConsoleTests/excel.fs
================================================
// Wrappers for Excel functions to be used in the testcases
#light
namespace Excel.FinancialFunctions
open Microsoft.Office.Interop.Excel // You need Excel 12 to test this
open System
open System.Runtime.InteropServices // For COMException
open Excel.FinancialFunctions.Common
open Excel.FinancialFunctions.Tvm
open Excel.FinancialFunctions.DayCount
module internal ExcelTesting =
// Need to be a singleton for perf reasons
let app = new ApplicationClass()
let funcs = app.WorksheetFunction
let pvEx r nper pmt fv (pd:PaymentDue) = funcs.Pv(r, nper, pmt, fv, int pd)
let fvEx r nper pmt thePv (pd:PaymentDue) = funcs.Fv(r, nper, pmt, thePv, int pd)
let pmtEx r nper pv fv (pd:PaymentDue) = funcs.Pmt(r, nper, pv, fv, int pd)
let ipmtEx r per nper pv fv (pd:PaymentDue) = funcs.Ipmt(r, per,
gitextract_v0ky_s4e/
├── .config/
│ └── dotnet-tools.json
├── .gitattributes
├── .github/
│ ├── ISSUE_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── pull-requests.yml
│ ├── push-master.yml
│ └── release.yml
├── .gitignore
├── ExcelFinancialFunctions.sln
├── LICENSE.txt
├── PackageReadmeFile.md
├── README.md
├── RELEASE_NOTES.md
├── docs/
│ ├── compatibility.fsx
│ ├── index.fsx
│ └── openofficediff.fsx
├── src/
│ └── ExcelFinancialFunctions/
│ ├── ExcelFinancialFunctions.fsproj
│ ├── ExcelFinancialFunctions.snk
│ ├── bonds.fs
│ ├── common.fs
│ ├── daycountbasis.fs
│ ├── depreciation.fs
│ ├── irr.fs
│ ├── loan.fs
│ ├── misc.fs
│ ├── oddbonds.fs
│ ├── publicenums.fs
│ ├── tbill.fs
│ ├── testpreconditions.fs
│ ├── tvm.fs
│ └── wrapperdotnettype.fs
└── tests/
├── ExcelFinancialFunctions.ConsoleTests/
│ ├── ExcelFinancialFunctions.ConsoleTests.fsproj
│ ├── MatrixTests.fs
│ ├── README.md
│ ├── SpotTests.fs
│ ├── excel.fs
│ ├── testinfrastructure.fs
│ └── testsdef.fs
├── ExcelFinancialFunctions.ReleaseTests/
│ ├── ExcelFinancialFunctions.ReleaseTests.csproj
│ ├── README.md
│ └── SpotTests.cs
└── ExcelFinancialFunctions.Tests/
├── ExcelFinancialFunctions.Tests.fsproj
├── Program.fs
├── concurrencytests.fs
├── crosstests.fs
├── spottests.fs
├── testdata/
│ ├── accrint.test
│ ├── accrintm.test
│ ├── amordegrc.test
│ ├── amorlinc.test
│ ├── coupdays.test
│ ├── coupdaysbs.test
│ ├── coupdaysnc.test
│ ├── coupncd.test
│ ├── coupnum.test
│ ├── couppcd.test
│ ├── cumipmt.test
│ ├── cumprinc.test
│ ├── db.test
│ ├── ddb.test
│ ├── disc.test
│ ├── dollarde.test
│ ├── dollarfr.test
│ ├── duration.test
│ ├── effect.test
│ ├── fv.test
│ ├── fvschedule.test
│ ├── intrate.test
│ ├── ipmt.test
│ ├── irr.test
│ ├── ispmt.test
│ ├── mduration.test
│ ├── mirr.test
│ ├── nominal.test
│ ├── nper.test
│ ├── npv.test
│ ├── oddfprice.test
│ ├── oddfyield.test
│ ├── oddlprice.test
│ ├── oddlyield.test
│ ├── pduration.csv
│ ├── pmt.test
│ ├── ppmt.test
│ ├── price.test
│ ├── pricedisc.test
│ ├── pricemat.test
│ ├── pv.test
│ ├── rate.test
│ ├── received.test
│ ├── rri.csv
│ ├── sln.test
│ ├── syd.test
│ ├── tbilleq.test
│ ├── tbillprice.test
│ ├── tbillyield.test
│ ├── testdata.xlsx
│ ├── vdb.test
│ ├── xirr.test
│ ├── yearfrac.test
│ ├── yielddisc.test
│ ├── yieldmat.test
│ ├── yieldnegative.csv
│ └── yieldnegativefails.csv
└── testutils.fs
SYMBOL INDEX (17 symbols across 1 files)
FILE: tests/ExcelFinancialFunctions.ReleaseTests/SpotTests.cs
class SpotTests (line 7) | [DefaultFloatingPointTolerance(1e-6)]
method Readme1 (line 10) | [Test(ExpectedResult = -796.374758)]
method Readme2 (line 14) | [Test(ExpectedResult = -1687.713656)]
method YieldIssue8 (line 18) | [Test(ExpectedResult = -0.67428578540657702)]
method spotYield (line 22) | [Test(ExpectedResult = 0.065)]
method spotXnpv (line 26) | [Test(ExpectedResult = 1.375214)]
method CoupDays (line 30) | [Test(ExpectedResult = 90.0)]
method CoupDaysBS (line 34) | [Test(ExpectedResult = 59.0)]
method CoupDaysNC (line 38) | [Test(ExpectedResult = 31.0)]
method CoupNum (line 42) | [Test(ExpectedResult = 25.0)]
method DollarDe (line 46) | [Test(ExpectedResult = 1.78125)]
method DollarFr (line 50) | [Test(ExpectedResult = 1.02)]
method Effect (line 54) | [Test(ExpectedResult = 0.05354266737)]
method FvSchedule (line 58) | [Test(ExpectedResult = 121.5236352)]
method Irr (line 62) | [Test(ExpectedResult = 0.260952337)]
method ISPmt (line 66) | [Test(ExpectedResult = -10.5)]
method Mirr (line 70) | [Test(ExpectedResult = 0.2409336873)]
Copy disabled (too large)
Download .json
Condensed preview — 104 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (20,247K chars).
[
{
"path": ".config/dotnet-tools.json",
"chars": 153,
"preview": "{\n \"version\": 1,\n \"isRoot\": true,\n \"tools\": {\n \"fsdocs-tool\": {\n \"version\": \"20.0.0\",\n \"commands\": [\n "
},
{
"path": ".gitattributes",
"chars": 2518,
"preview": "###############################################################################\n# Set default behavior to automatically "
},
{
"path": ".github/ISSUE_TEMPLATE.md",
"chars": 580,
"preview": "\n### Description\n\nPlease provide a succinct description of your issue.\n\n### Repro steps\n\nPlease provide the steps requir"
},
{
"path": ".github/dependabot.yml",
"chars": 114,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"nuget\"\n directory: \"/\"\n schedule:\n interval: \"daily\"\n "
},
{
"path": ".github/workflows/pull-requests.yml",
"chars": 447,
"preview": "name: Pull Request\n\non:\n pull_request:\n branches: [ master ]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n "
},
{
"path": ".github/workflows/push-master.yml",
"chars": 954,
"preview": "name: Build+Test+Docs\n\non:\n push:\n branches: [ master ]\n\njobs:\n build:\n runs-on: ubuntu-latest\n steps:\n - "
},
{
"path": ".github/workflows/release.yml",
"chars": 985,
"preview": "name: Release\n\non:\n release:\n types:\n - published\n\n workflow_dispatch:\n\njobs:\n build:\n runs-on: ubuntu-lat"
},
{
"path": ".gitignore",
"chars": 2781,
"preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n\n# User"
},
{
"path": "ExcelFinancialFunctions.sln",
"chars": 3407,
"preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.3170"
},
{
"path": "LICENSE.txt",
"chars": 9956,
"preview": "Copyright 2010-2013\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in"
},
{
"path": "PackageReadmeFile.md",
"chars": 3903,
"preview": "# Excel Financial Functions\n\nThis is a .NET Standard library that provides the full set of financial functions from Exce"
},
{
"path": "README.md",
"chars": 6504,
"preview": "# Excel Financial Functions\n\nThis is a .NET Standard library that provides the full set of financial functions from Exce"
},
{
"path": "RELEASE_NOTES.md",
"chars": 1511,
"preview": "## 3.2.0 - May 10 2022\n* Removes needless constraint on 0-value inputs to FV & PMT functions. (PR #67)\n\n## 3.1.0 - Dec 2"
},
{
"path": "docs/compatibility.fsx",
"chars": 5562,
"preview": "(*** hide ***)\n#I \"../src/ExcelFinancialFunctions/bin/Release/netstandard2.0\"\n#r \"ExcelFinancialFunctions.dll\"\n\nopen Sys"
},
{
"path": "docs/index.fsx",
"chars": 3328,
"preview": "(*** hide ***)\n// This block of code is omitted in the generated HTML documentation. Use \n// it to define helpers that y"
},
{
"path": "docs/openofficediff.fsx",
"chars": 7031,
"preview": "(*** hide ***)\n#I \"../src/ExcelFinancialFunctions/bin/Release/netstandard2.0\"\n#r \"ExcelFinancialFunctions.dll\"\n\nopen Sys"
},
{
"path": "src/ExcelFinancialFunctions/ExcelFinancialFunctions.fsproj",
"chars": 2139,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFramework>netstandard2.0</TargetFramework>\n <Generat"
},
{
"path": "src/ExcelFinancialFunctions/bonds.fs",
"chars": 13997,
"preview": "// Bonds mathematics the Excel way \n#light\nnamespace Excel.FinancialFunctions\n\nopen Excel.FinancialFunctions.Common\nopen"
},
{
"path": "src/ExcelFinancialFunctions/common.fs",
"chars": 6483,
"preview": "// Common math, error management, zero finding, etc... routines used in all the rest of the library\n#light\nnamespace Exc"
},
{
"path": "src/ExcelFinancialFunctions/daycountbasis.fs",
"chars": 14667,
"preview": "// Messy, but excel compatible, treatment of day count conventions for bond mathematics.\n// I tried to abstract out the "
},
{
"path": "src/ExcelFinancialFunctions/depreciation.fs",
"chars": 12033,
"preview": "// Depreciation calculations. AmorDegr and AmorLinc required a lot of work and trial and error. I wonder how many people"
},
{
"path": "src/ExcelFinancialFunctions/irr.fs",
"chars": 3251,
"preview": "// Finding internal rate of return routines. I use a different algo then excel. The results might be different.\n#light\nn"
},
{
"path": "src/ExcelFinancialFunctions/loan.fs",
"chars": 6440,
"preview": "// Loan related calculation routines, small variations on TVM\n#light\nnamespace Excel.FinancialFunctions\n\nopen System\nope"
},
{
"path": "src/ExcelFinancialFunctions/misc.fs",
"chars": 2034,
"preview": "// Various routings that don't have an obvious classification in other files.\n#light\nnamespace Excel.FinancialFunctions\n"
},
{
"path": "src/ExcelFinancialFunctions/oddbonds.fs",
"chars": 10294,
"preview": "// Difficult to digest formulas for odd bonds calculations. Trust the testcases that I got these ones right.\n#light\nname"
},
{
"path": "src/ExcelFinancialFunctions/publicenums.fs",
"chars": 1105,
"preview": "// All the enums exposed in the external API, I need to define them first becasue they are used in the internal part\n#li"
},
{
"path": "src/ExcelFinancialFunctions/tbill.fs",
"chars": 3399,
"preview": "// Very simple TBill mathematics. The only interesting thing is the 'if' statement on line 17.\n#light\nnamespace Excel.Fi"
},
{
"path": "src/ExcelFinancialFunctions/testpreconditions.fs",
"chars": 14484,
"preview": "namespace Excel.FinancialFunctions\n\n[<assembly:System.Runtime.CompilerServices.InternalsVisibleTo \"ExcelFinancialFuncti"
},
{
"path": "src/ExcelFinancialFunctions/tvm.fs",
"chars": 4273,
"preview": "// Time value of money routines, note the extensive treatment of error condition to help the user with sensible error me"
},
{
"path": "src/ExcelFinancialFunctions/wrapperdotnettype.fs",
"chars": 25341,
"preview": "// I cannot use F# optional parameters because they end up forcing you to include the F# dll\n// Instead I use overloadin"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/ExcelFinancialFunctions.ConsoleTests.fsproj",
"chars": 871,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n <PropertyGroup>\n <TargetFramework>net48</TargetFramework> \n <GenerateDocument"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/MatrixTests.fs",
"chars": 9507,
"preview": "namespace Excel.FinancialFunctions\n\nopen NUnit.Framework\nopen TestInfrastructure\nopen Excel.FinancialFunctions.ExcelTes"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/README.md",
"chars": 1764,
"preview": "# Console Tests\n\nThese tests require Excel 2013 or later installed on your host machine. They directly\ncompare the resul"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/SpotTests.fs",
"chars": 12411,
"preview": "namespace Excel.FinancialFunctions\n\nopen System\nopen NUnit.Framework\nopen TestInfrastructure\nopen Excel.FinancialFuncti"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/excel.fs",
"chars": 6972,
"preview": "// Wrappers for Excel functions to be used in the testcases\n#light\nnamespace Excel.FinancialFunctions\nopen Microsoft.Off"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/testinfrastructure.fs",
"chars": 6742,
"preview": "// Test infrastructure. The idea is that Excel is the oracle.\n// I test a whole bunch of different values for parameters"
},
{
"path": "tests/ExcelFinancialFunctions.ConsoleTests/testsdef.fs",
"chars": 7816,
"preview": "// All the test values that I want to throw at the testcase infrastructure. Just add values here if you want to run 1,00"
},
{
"path": "tests/ExcelFinancialFunctions.ReleaseTests/ExcelFinancialFunctions.ReleaseTests.csproj",
"chars": 478,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFramework>net461</TargetFramework>\n\n <IsPackable>fal"
},
{
"path": "tests/ExcelFinancialFunctions.ReleaseTests/README.md",
"chars": 746,
"preview": "# Release Tests\n\nThis project tests new release candidate nuget packages after they are deployed to the NuGet Gallery. I"
},
{
"path": "tests/ExcelFinancialFunctions.ReleaseTests/SpotTests.cs",
"chars": 3125,
"preview": "using NUnit.Framework;\nusing Excel.FinancialFunctions;\nusing System;\n\nnamespace ExcelFinancialFunctions.ReleaseTests\n{\n "
},
{
"path": "tests/ExcelFinancialFunctions.Tests/ExcelFinancialFunctions.Tests.fsproj",
"chars": 1072,
"preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n\n <PropertyGroup>\n <TargetFramework>netcoreapp6.0</TargetFramework>\n\n <Generate"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/Program.fs",
"chars": 56,
"preview": "module Program =\n\n [<EntryPoint>]\n let main _ = 0\n"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/concurrencytests.fs",
"chars": 2167,
"preview": "namespace Excel.FinancialFunctions.Tests\n\nopen NUnit.Framework\n\n// These tests check that nothing goes wrong with the m"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/crosstests.fs",
"chars": 7625,
"preview": "#nowarn \"25\"\n\nnamespace Excel.FinancialFunctions.Tests\n\nopen NUnit.Framework\nopen System.IO\nopen System\n\n[<SetCulture(\""
},
{
"path": "tests/ExcelFinancialFunctions.Tests/spottests.fs",
"chars": 4532,
"preview": "namespace Excel.FinancialFunctions.Tests\n\nopen FsCheck\nopen NUnit.Framework\n\n[<SetCulture(\"en-US\")>]\n[<DefaultFloatingP"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/accrint.test",
"chars": 213015,
"preview": "3/4/1990 12:00:00 AM,3/31/1993 12:00:00 AM,3/4/1992 12:00:00 AM,0.07,10000,Annual,Actual360,1400\n3/4/1990 12:00:00 AM,3/"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/accrintm.test",
"chars": 29079,
"preview": "3/4/1990 12:00:00 AM,3/4/1992 12:00:00 AM,0.07,10000,Actual360,1421.388888889\n3/4/1990 12:00:00 AM,3/4/1992 12:00:00 AM,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/amordegrc.test",
"chars": 1782578,
"preview": "100,2/28/1998 12:00:00 AM,2/29/2000 12:00:00 AM,10,0,0.15,Actual365,True,75\n100,2/28/1998 12:00:00 AM,2/29/2000 12:00:00"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/amorlinc.test",
"chars": 844478,
"preview": "100,2/28/1998 12:00:00 AM,2/29/2000 12:00:00 AM,10,0,0.07,Actual365,14\n100,2/28/1998 12:00:00 AM,2/29/2000 12:00:00 AM,1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/coupdays.test",
"chars": 1165,
"preview": "2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,Actual360,360\n2/14/2003 12:00:00 AM,5/14/2003 12:00:00 AM,Annual,Actu"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/coupdaysbs.test",
"chars": 62505,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actual360,352\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actu"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/coupdaysnc.test",
"chars": 62510,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actual360,13\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actua"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/coupncd.test",
"chars": 72992,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actual360,6.24561408E+17\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/coupnum.test",
"chars": 62070,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actual360,21\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actua"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/couppcd.test",
"chars": 73134,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,Annual,Actual360,6.24246048E+17\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/cumipmt.test",
"chars": 9696,
"preview": "0.6,2,100,1,1.2,BeginningOfPeriod,0\n0.6,2,100,1,1.2,EndOfPeriod,-60\n0.6,2,100,1,2,BeginningOfPeriod,-23.07692307692\n0.6,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/cumprinc.test",
"chars": 9946,
"preview": "0.6,2,100,1,1.2,BeginningOfPeriod,-61.53846153846\n0.6,2,100,1,1.2,EndOfPeriod,-38.46153846154\n0.6,2,100,1,2,BeginningOfP"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/db.test",
"chars": 10297,
"preview": "100,10,1,0.3,1,7.5\n100,10,1,0.3,4,30\n100,10,1,0.3,9,67.5\n100,10,1,1,1,7.5\n100,10,1,1,4,30\n100,10,1,1,9,67.5\n100,10,13,0."
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/ddb.test",
"chars": 11295,
"preview": "100,10,1,0.3,1,90\n100,10,1,0.3,3,90\n100,10,1,0.3,4.5,90\n100,10,1,0.3,50.3,90\n100,10,1,1,1,90\n100,10,1,1,3,90\n100,10,1,1,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/disc.test",
"chars": 214499,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actual360,0\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actu"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/dollarde.test",
"chars": 198,
"preview": "0.34,1,0.34\n0.34,17,2\n0.34,20,1.7\n1.02,1,1.02\n1.02,17,1.117647058824\n1.02,20,1.1\n2.34,1,2.34\n2.34,17,4\n2.34,20,3.7\n-1.5,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/dollarfr.test",
"chars": 191,
"preview": "0.34,1,0.34\n0.34,17,0.0578\n0.34,20,0.068\n1.02,1,1.02\n1.02,17,1.0034\n1.02,20,1.004\n2.34,1,2.34\n2.34,17,2.0578\n2.34,20,2.0"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/duration.test",
"chars": 482989,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,0.03,Annual,Actual360,8.949173397837\n2/15/1980 12:00:00 AM,2/28/2000 12:"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/effect.test",
"chars": 257,
"preview": "0.6,1,0.6\n0.6,1.7,0.6\n0.6,2,0.69\n0.6,10,0.7908476965429\n0.6,11.3,0.793570056309\n0.6,13,0.7978035299639\n1.5,1,1.5\n1.5,1.7"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/fv.test",
"chars": 73683,
"preview": "-1.5,-2,50,-300,BeginningOfPeriod,1150\n-1.5,-2,50,-300,EndOfPeriod,1300\n-1.5,-2,50,-100,BeginningOfPeriod,350\n-1.5,-2,50"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/fvschedule.test",
"chars": 466,
"preview": "-300,0.3;-0.5;0.2;1.3;-0.2,-430.56\n-300,0.3;-0.5;0.2;0;-1.2,46.8\n-100,0.3;-0.5;0.2;1.3;-0.2,-143.52\n-100,0.3;-0.5;0.2;0;"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/intrate.test",
"chars": 213765,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actual360,0\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actu"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/ipmt.test",
"chars": 197058,
"preview": "-0.4,1,1,-300,-300,BeginningOfPeriod,0\n-0.4,1,1,-300,-300,EndOfPeriod,-120\n-0.4,1,1,-300,-100,BeginningOfPeriod,0\n-0.4,1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/irr.test",
"chars": 381,
"preview": "-100;10;10;100,0.15,0.06886017912484\n-100;10;10;100,0.5,0.06886017912484\n-100;10;10;100,0,0.06886017912484\n-100;-10;10;1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/ispmt.test",
"chars": 12515,
"preview": "-1.5,1,1,-300,0\n-1.5,1,1,-100,0\n-1.5,1,1,-5.4,0\n-1.5,1,1,0,0\n-1.5,1,1,100,0\n-1.5,1,1,150.5,0\n-1.5,1,2,-300,-225\n-1.5,1,2"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/mduration.test",
"chars": 483628,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,0.03,Annual,Actual360,8.688517861978\n2/15/1980 12:00:00 AM,2/28/2000 12:"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/mirr.test",
"chars": 6008,
"preview": "-100;10;10;100,-1.5,-1.5,-0.008403758659613\n-100;10;10;100,-1.5,-2,0\n-100;10;10;100,-1.5,-0.4,0.03102734709515\n-100;10;1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/nominal.test",
"chars": 285,
"preview": "0.6,1,0.6\n0.6,1.7,0.6\n0.6,2,0.5298221281347\n0.6,10,0.4812238946896\n0.6,11.3,0.4801892443735\n0.6,13,0.4786032384269\n1.5,1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/nper.test",
"chars": 36825,
"preview": "-0.4,50,-300,-100,EndOfPeriod,5.54634147592\n-0.4,50,-300,-5.4,BeginningOfPeriod,3.296940052094\n-0.4,50,-300,-5.4,EndOfPe"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/npv.test",
"chars": 696,
"preview": "-1.5,-100;10;10;100,1760\n-1.5,-100;-10;10;100,1680\n-1.5,-200;0;10;-10;300,-9440\n-2,-100;10;10;100,200\n-2,-100;-10;10;100"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/oddfprice.test",
"chars": 2635538,
"preview": "2/28/1999 12:00:00 AM,6/30/2010 12:00:00 AM,2/28/1998 12:00:00 AM,6/30/2009 12:00:00 AM,0.07,0.03,100,Annual,Actual360,1"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/oddfyield.test",
"chars": 1124,
"preview": "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,UsPsa"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/oddlprice.test",
"chars": 3492756,
"preview": "2/28/1999 12:00:00 AM,2/28/2000 12:00:00 AM,2/28/1998 12:00:00 AM,0.07,0.03,100,Annual,Actual360,103.6796116505\n2/28/199"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/oddlyield.test",
"chars": 5287146,
"preview": "2/28/1999 12:00:00 AM,2/28/2000 12:00:00 AM,2/28/1998 12:00:00 AM,0.07,100,100,Annual,Actual360,0.06542056074766\n2/28/19"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/pduration.csv",
"chars": 789,
"preview": "rate,pv,fv,pduration\n0.01530947049973120,-5,-6,#NUM!\n-1.00000000000000000,-5,0,#NUM!\n0.00000000000000000,-1,-1,#NUM!\n0.0"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/pmt.test",
"chars": 74250,
"preview": "-0.4,-2,-300,-300,BeginningOfPeriod,-425\n-0.4,-2,-300,-300,EndOfPeriod,-255\n-0.4,-2,-300,-100,BeginningOfPeriod,-350\n-0."
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/ppmt.test",
"chars": 211832,
"preview": "-0.4,1,1,-300,-300,BeginningOfPeriod,800\n-0.4,1,1,-300,-300,EndOfPeriod,600\n-0.4,1,1,-300,-100,BeginningOfPeriod,466.666"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/price.test",
"chars": 1013982,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,0.07,0.03,100,Annual,Actual360,159.5561168405\n2/15/1980 12:00:00 AM,2/28/200"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/pricedisc.test",
"chars": 285905,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,0.01,100,Actual360,79.67222222222\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 A"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/pricemat.test",
"chars": 196224,
"preview": "12/31/1993 12:00:00 AM,2/28/2000 12:00:00 AM,3/4/1990 12:00:00 AM,0.07,0.03,Actual360,116.7605263158\n12/31/1993 12:00:00"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/pv.test",
"chars": 68458,
"preview": "-1.5,-2,50,-300,BeginningOfPeriod,62.5\n-1.5,-2,50,-300,EndOfPeriod,100\n-1.5,-2,50,-100,BeginningOfPeriod,12.5\n-1.5,-2,50"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/rate.test",
"chars": 384,
"preview": "1,10,100,-100,EndOfPeriod,0.15,-0.1\n1,10,100,-100,EndOfPeriod,0.15,-0.1\n5,20,120,-50,BeginningOfPeriod,0,-0.335618541452"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/received.test",
"chars": 101406,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,0.01,Actual360,125.5142598145\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 A"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/rri.csv",
"chars": 571,
"preview": "nper,pv,fv,rri\n0,300,400,#NUM!\n0,-1,-3,#NUM!\n1,-1,-3,2\n12,100,10,-0.174595815\n12,100,-90,#NUM!\n5,0,0,0\n5,-1,5,#NUM!\n5,10"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/sln.test",
"chars": 494,
"preview": "100,10,1,90\n100,10,13,6.923076923077\n100,10,12.7,7.086614173228\n100,10,40,2.25\n100,50,1,50\n100,50,13,3.846153846154\n100,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/syd.test",
"chars": 3662,
"preview": "100,10,1,0.3,153\n100,10,1,1,90\n100,10,13,0.3,13.54945054945\n100,10,13,1,12.85714285714\n100,10,13,1.7,12.16483516484\n100,"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/tbilleq.test",
"chars": 3253,
"preview": "2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.01,0.01014706291179\n2/15/1980 12:00:00 AM,3/31/1980 12:00:00 AM,0.01,0.010"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/tbillprice.test",
"chars": 4941,
"preview": "2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.01,99.91944444444\n2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,0.25,97.9861"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/tbillyield.test",
"chars": 4197,
"preview": "2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,75,4.137931034483\n2/15/1980 12:00:00 AM,3/15/1980 12:00:00 AM,100,0\n2/15/198"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/vdb.test",
"chars": 129500,
"preview": "100,10,1,0,0.8,1,DontSwitchToStraightLine,72\n100,10,1,0,0.8,1,SwitchToStraightLine,72\n100,10,1,0,0.8,3,DontSwitchToStrai"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/xirr.test",
"chars": 3864,
"preview": "-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.01563509255"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/yearfrac.test",
"chars": 96625,
"preview": "3/4/1980 12:00:00 AM,3/5/1980 12:00:00 AM,Actual360,0.002777777777778\n3/4/1980 12:00:00 AM,3/5/1980 12:00:00 AM,Actual36"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/yielddisc.test",
"chars": 213599,
"preview": "2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actual360,0\n2/15/1980 12:00:00 AM,2/28/2000 12:00:00 AM,100,100,Actu"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/yieldmat.test",
"chars": 297337,
"preview": "12/31/1993 12:00:00 AM,2/28/2000 12:00:00 AM,3/4/1990 12:00:00 AM,0.07,75,Actual360,0.1076496493231\n12/31/1993 12:00:00 "
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/yieldnegative.csv",
"chars": 269088,
"preview": "settlement,maturity,rate,yield,redemption,frequency,basis,price\n2/15/1980,6/30/2010,0.07,-0.00297404851120256,130,Quarte"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testdata/yieldnegativefails.csv",
"chars": 421801,
"preview": "settlement,maturity,rate,yield,redemption,frequency,basis,price\n12/31/1993,2/29/2008,0.07,-0.05706302555203390,100,Annua"
},
{
"path": "tests/ExcelFinancialFunctions.Tests/testutils.fs",
"chars": 2887,
"preview": "#nowarn \"25\"\n\nnamespace Excel.FinancialFunctions.Tests\n\nopen FsCheck\nopen NUnit.Framework\nopen System\nopen System.IO\n\n["
}
]
// ... and 2 more files (download for full content)
About this extraction
This page contains the full source code of the fsprojects/ExcelFinancialFunctions GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 104 files (19.1 MB), approximately 5.0M tokens, and a symbol index with 17 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.