master 97756a375769 cached
104 files
19.1 MB
5.0M tokens
17 symbols
1 requests
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.

[![NuGet Badge](https://img.shields.io/nuget/v/ExcelFinancialFunctions.svg?style=flat)](https://www.nuget.org/packages/ExcelFinancialFunctions/)
[![Build+Test+Docs](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml/badge.svg)](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml)

## 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.

[![Build+Test+Docs](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml/badge.svg)](https://github.com/fsprojects/ExcelFinancialFunctions/actions/workflows/push-master.yml)
[![NuGet Badge](https://img.shields.io/nuget/v/ExcelFinancialFunctions.svg?style=flat)](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>. [![NuGet Status](//img.shields.io/nuget/v/ExcelFinancialFunctions?style=flat)](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,
Download .txt
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
Download .txt
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.

Copied to clipboard!