The library was designed to be Excel-compliant (see Compatibility 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.
According to the OO wiki,
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)
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)
Excel: 127.9031274
The functions return different results. ODDLYIELD example can be found here.
Financial.OddLPrice(DateTime(1999, 2, 28), DateTime(2000, 2, 28), DateTime(1998, 2, 28),
0.07, 0.03, 130., Frequency.SemiAnnual, DayCountBasis.Actual360)
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)
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)
Excel: -0.00327205 LibreOffice: -0.002925876
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
Excel: 1401.944444 LibreOffice: 1400.000000
accrint DayCountBasis.ActualActual
Excel: 1398.076923 LibreOffice: 1401.917808
accrint DayCountBasis.Actual360
Excel: 1394.166667 LibreOffice: 1421.388889
accrint DayCountBasis.Actual365
Excel: 1399.041096 LibreOffice: 1401.917808
Financial.AccrIntM(DateTime(1990, 3, 4), DateTime(2010, 6, 5), 0.1, 12030.34, DayCountBasis.ActualActual)
Excel: 24367.7909 LibreOffice: 24383.68639
Financial.Disc(DateTime(2003, 2, 14), DateTime(2004, 3, 31), 23., 100., DayCountBasis.ActualActual)
Excel: 0.684757 LibreOffice: 0.683820
Financial.Duration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03,
Frequency.Annual, DayCountBasis.Actual360)
Excel: 8.949173 LibreOffice: 9.254729
Financial.MDuration(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 100., 0.03,
Frequency.SemiAnnual, DayCountBasis.Actual360)
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)
Excel: 74.442516 LibreOffice: 74.334983
Financial.PriceDisc(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 0.01, 100., DayCountBasis.ActualActual)
Excel: 79.966367 LibreOffice: 80.005464
Financial.IntRate(DateTime(1980, 2, 15), DateTime(1980, 5, 4), 23., 130., DayCountBasis.UsPsa30_360)
Excel: 21.199780 LibreOffice: 21.471572
Financial.TBillPrice(DateTime(1980, 2, 15), DateTime(1980, 3, 15), 2.)
Excel: 83.888889 LibreOffice: 82.777778
Financial.Received(DateTime(1980, 2, 15), DateTime(2000, 2, 28), 200., 0.01, DayCountBasis.ActualActual)
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)
Excel: 0 Excel for Mac: -2 LibreOffice: 75
As stated here,
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)
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)
Excel: 0.0000000 LibreOffice: -63.33333
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
Excel: 360 <> 345 + 13 LibreOffice: 360 = 345 + 15
OO analogs are called CUMIPMT_ADD and CUMPRINC_ADD (they're expected to be compatible with Excel)
Financial.CumIPmt(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod)
Excel: -59.669577 LibreOffice: -119.669577
Financial.CumPrinc(0.6, 10., 100., 1.3, 2., PaymentDue.EndOfPeriod)
Excel: -0.8811289 LibreOffice: -1.431834
Seems like DDB doesn't accept fractional periods.
Financial.Db(100., 10., 1., 0.3, 1.)
Excel: 7.5 LibreOffice: 0.0
Financial.Ddb(100., 10., 1., 0.3, 1.)
Excel: 90.0 LibreOffice: Err:502
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)
Excel: -0.960452 LibreOffice: Err:502 (Invalid argument)
namespace System
namespace Excel
namespace Excel.FinancialFunctions
type Financial =
static member AccrInt : issue:DateTime * firstInterest:DateTime * settlement:DateTime * rate:float * par:float * frequency:Frequency * basis:DayCountBasis * calcMethod:AccrIntCalcMethod -> float + 1 overload
static member AccrIntM : issue:DateTime * settlement:DateTime * rate:float * par:float * basis:DayCountBasis -> float
static member AmorDegrc : cost:float * datePurchased:DateTime * firstPeriod:DateTime * salvage:float * period:float * rate:float * basis:DayCountBasis * excelCompliant:bool -> float
static member AmorLinc : cost:float * datePurchased:DateTime * firstPeriod:DateTime * salvage:float * period:float * rate:float * basis:DayCountBasis -> float
static member CoupDays : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member CoupDaysBS : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member CoupDaysNC : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member CoupNCD : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> DateTime
static member CoupNum : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member CoupPCD : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> DateTime
...
<summary>
A wrapper class to expose the Excel financial functions API to .NET clients
</summary>
static member Financial.OddFYield : settlement:DateTime * maturity:DateTime * issue:DateTime * firstCoupon:DateTime * rate:float * pr:float * redemption:float * frequency:Frequency * basis:DayCountBasis -> float
Multiple items
[<Struct>]
type DateTime =
new : year: int * month: int * day: int -> unit + 10 overloads
member Add : value: TimeSpan -> DateTime
member AddDays : value: float -> DateTime
member AddHours : value: float -> DateTime
member AddMilliseconds : value: float -> DateTime
member AddMinutes : value: float -> DateTime
member AddMonths : months: int -> DateTime
member AddSeconds : value: float -> DateTime
member AddTicks : value: int64 -> DateTime
member AddYears : value: int -> DateTime
...
<summary>Represents an instant in time, typically expressed as a date and time of day.</summary>
--------------------
DateTime ()
(+0 other overloads)
DateTime(ticks: int64) : DateTime
(+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : DateTime
(+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : DateTime
(+0 other overloads)
type Frequency =
| Annual = 1
| SemiAnnual = 2
| Quarterly = 4
<summary>
The number of coupon payments per year
</summary>
Frequency.Quarterly: Frequency = 4
type DayCountBasis =
| UsPsa30_360 = 0
| ActualActual = 1
| Actual360 = 2
| Actual365 = 3
| Europ30_360 = 4
<summary>
The type of Day Count Basis
</summary>
DayCountBasis.ActualActual: DayCountBasis = 1
<summary>
Actual/Actual
</summary>
static member Financial.OddFPrice : settlement:DateTime * maturity:DateTime * issue:DateTime * firstCoupon:DateTime * rate:float * yld:float * redemption:float * frequency:Frequency * basis:DayCountBasis -> float
Frequency.Annual: Frequency = 1
DayCountBasis.Actual360: DayCountBasis = 2
<summary>
Actual/360
</summary>
static member Financial.OddLPrice : settlement:DateTime * maturity:DateTime * lastInterest:DateTime * rate:float * yld:float * redemption:float * frequency:Frequency * basis:DayCountBasis -> float
Frequency.SemiAnnual: Frequency = 2
static member Financial.OddLYield : settlement:DateTime * maturity:DateTime * lastInterest:DateTime * rate:float * pr:float * redemption:float * frequency:Frequency * basis:DayCountBasis -> float
val accrint : basis:DayCountBasis -> float
val basis : DayCountBasis
static member Financial.AccrInt : issue:DateTime * firstInterest:DateTime * settlement:DateTime * rate:float * par:float * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.AccrInt : issue:DateTime * firstInterest:DateTime * settlement:DateTime * rate:float * par:float * frequency:Frequency * basis:DayCountBasis * calcMethod:AccrIntCalcMethod -> float
DayCountBasis.UsPsa30_360: DayCountBasis = 0
<summary>
US 30/360
</summary>
DayCountBasis.Actual365: DayCountBasis = 3
<summary>
Actual/365
</summary>
static member Financial.AccrIntM : issue:DateTime * settlement:DateTime * rate:float * par:float * basis:DayCountBasis -> float
static member Financial.Disc : settlement:DateTime * maturity:DateTime * pr:float * redemption:float * basis:DayCountBasis -> float
static member Financial.Duration : settlement:DateTime * maturity:DateTime * coupon:float * yld:float * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.MDuration : settlement:DateTime * maturity:DateTime * coupon:float * yld:float * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.Price : settlement:DateTime * maturity:DateTime * rate:float * yld:float * redemption:float * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.PriceDisc : settlement:DateTime * maturity:DateTime * discount:float * redemption:float * basis:DayCountBasis -> float
static member Financial.IntRate : settlement:DateTime * maturity:DateTime * investment:float * redemption:float * basis:DayCountBasis -> float
static member Financial.TBillPrice : settlement:DateTime * maturity:DateTime * discount:float -> float
static member Financial.Received : settlement:DateTime * maturity:DateTime * investment:float * discount:float * basis:DayCountBasis -> float
static member Financial.AmorDegrc : cost:float * datePurchased:DateTime * firstPeriod:DateTime * salvage:float * period:float * rate:float * basis:DayCountBasis * excelCompliant:bool -> float
static member Financial.AmorLinc : cost:float * datePurchased:DateTime * firstPeriod:DateTime * salvage:float * period:float * rate:float * basis:DayCountBasis -> float
val cdParam : DateTime * DateTime * Frequency * DayCountBasis
static member Financial.CoupDays : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.CoupDaysBS : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.CoupDaysNC : settlement:DateTime * maturity:DateTime * frequency:Frequency * basis:DayCountBasis -> float
static member Financial.CumIPmt : rate:float * nper:float * pv:float * startPeriod:float * endPeriod:float * typ:PaymentDue -> float
type PaymentDue =
| EndOfPeriod = 0
| BeginningOfPeriod = 1
<summary>
Indicates when payments are due (end/beginning of period)
</summary>
PaymentDue.EndOfPeriod: PaymentDue = 0
static member Financial.CumPrinc : rate:float * nper:float * pv:float * startPeriod:float * endPeriod:float * typ:PaymentDue -> float
static member Financial.Db : cost:float * salvage:float * life:float * period:float -> float
static member Financial.Db : cost:float * salvage:float * life:float * period:float * month:float -> float
static member Financial.Ddb : cost:float * salvage:float * life:float * period:float -> float
static member Financial.Ddb : cost:float * salvage:float * life:float * period:float * factor:float -> float
static member Financial.XIrr : values:seq<float> * dates:seq<DateTime> -> float
static member Financial.XIrr : values:seq<float> * dates:seq<DateTime> * guess:float -> float