FinanceCore
Documentation for FinanceCore.
Optional IRR vectorization
Loading LoopVectorization.jl enables an optimized inner kernel for compatible irr inputs. Backend selection depends only on the arguments to each call; loading LoopVectorization does not change a process-global FinanceCore setting.
The current public IRR solver uses the optimized kernel for dense Float64 cashflow vectors when timepoints are either a dense Float64 vector or the integer range created by irr(cashflows). Integer, mixed-type, custom-number, and Cashflow inputs use FinanceCore's base SIMD kernel.
The optimized and base kernels are numerically equivalent within floating-point precision. Reassociated operations and LoopVectorization's exponential implementation can produce differences near the last bit; pin the package environment and use tolerance-based comparisons when exact reproducibility is required across environments.
FinanceCore.CashflowFinanceCore.CompositeFinanceCore.ContinuousFinanceCore.ContinuousFinanceCore.PeriodicFinanceCore.PeriodicFinanceCore.QuoteFinanceCore.RateFinanceCore.TimepointBase.:*Base.:+Base.:-Base.:/Base.:<Base.convertBase.islessFinanceCore.accumulationFinanceCore.amountFinanceCore.compoundingFinanceCore.discountFinanceCore.internal_rate_of_returnFinanceCore.irrFinanceCore.present_valueFinanceCore.rateFinanceCore.timepoint
FinanceCore.Timepoint — Type
Timepoint(a)Summary ≡≡≡≡≡≡≡≡≡
Timepoint is a type alias for Union{T,Dates.Date} that can be used to represent a point in time. It can be either a Dates.Date or a Real number. If defined as a real number, the interpretation is the number of (fractional) periods since time zero.
Currently, the usage of Dates.Date is not well supported across the JuliaActuary ecosystem but this type is in place such that it can be built upon further.
Supertype Hierarchy ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
Timepoint{T} = Union{T,Dates.Date} <: AnyFinanceCore.Cashflow — Type
Cashflow(amount,time)A Cashflow{A,B} is a contract that pays an amount at time.
Cashflows can be:
- negated with the unary
-operator. - added/subtracted together but note that the
timemust beisapproxequal (defaultisapproxtolerance, i.e. relative to the magnitude of the times). - multiplied/divided by a scalar.
Supertype Hierarchy ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
Cashflow{A<:Real, B<:Timepoint} <: FinanceCore.AbstractContract <: AnyFinanceCore.Composite — Type
Composite(A,B)Summary ≡≡≡≡≡≡≡≡≡
struct Composite{A, B}A Composite{A,B} is a contract that is composed of two other contracts of type A and type B. The maturity of the composite is the maximum of the maturities of the two components.
It is used to assemble arbitrarily complex contracts from simpler ones.
Fields ≡≡≡≡≡≡≡≡
a :: A
b :: BSupertype Hierarchy ≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡≡
Composite{A, B} <: FinanceCore.AbstractContract <: AnyFinanceCore.Continuous — Type
Continuous()A type representing continuous interest compounding frequency.
Use rate to retrieve the nominal rate value from a Rate with Continuous compounding.
Examples
julia> Rate(0.01,Continuous())
Continuous(0.01)See also: Periodic
FinanceCore.Continuous — Method
Continuous(rate)A convenience constructor for Rate(rate, Continuous()). Use rate to retrieve the nominal rate value.
julia> Continuous(0.01)
Continuous(0.01)See also: Periodic
FinanceCore.Periodic — Type
Periodic(frequency)A type representing periodic interest compounding with the given frequency.
frequency will be converted to an Integer, and will round up to 8 decimal places (otherwise will throw an InexactError).
Use rate to retrieve the nominal rate value from a Rate with Periodic compounding.
Examples
Creating a semi-annual bond equivalent yield:
julia> Rate(0.01,Periodic(2))
Periodic(0.01, 2)See also: Continuous
FinanceCore.Periodic — Method
Periodic(rate, frequency)A convenience constructor for Rate(rate, Periodic(frequency)). Use rate to retrieve the nominal rate value.
Examples
Creating a semi-annual bond equivalent yield:
julia> Periodic(0.01,2)
Periodic(0.01, 2)See also: Continuous
FinanceCore.Quote — Type
Quote(price,instrument)The price(<:Real) is the observed value , and the instrument is the instrument/contract that the price is for.
This can be used, e.g., to calibrate a valuation model to prices for the given instruments - see FinanceModels.jl for more details.
FinanceCore.Rate — Method
Rate(rate[,frequency=1])
Rate(rate,frequency::Frequency)Rate is a type that encapsulates an interest rate along with its compounding frequency.
Internally, all rates (including Periodic rates) are stored as their continuously compounded equivalent for performance. This means the internal field values will differ from the nominal rate. Use rate to retrieve the nominal rate value corresponding to the compounding frequency, and compounding to retrieve the compounding frequency.
Periodic rates can be constructed via Rate(rate,frequency) or Rate(rate,Periodic(frequency)). If not given a second argument, Rate(rate) is equivalent to Rate(rate,Periodic(1)).
Continuous rates can be constructed via Rate(rate, Inf) or Rate(rate,Continuous()).
Examples
julia> Rate(0.01,Continuous())
Continuous(0.01)
julia> Continuous(0.01)
Continuous(0.01)
julia> Continuous()(0.01)
Continuous(0.01)
julia> Rate(0.01,Periodic(2))
Periodic(0.01, 2)
julia> Periodic(0.01,2)
Periodic(0.01, 2)
julia> Periodic(2)(0.01)
Periodic(0.01, 2)
julia> Rate(0.01)
Periodic(0.01, 1)
julia> Rate(0.01,2)
Periodic(0.01, 2)
julia> Rate(0.01,Periodic(4))
Periodic(0.01, 4)
julia> Rate(0.01,Inf)
Continuous(0.01)
julia> rate(Periodic(0.01,2))
0.01Base.:+ — Method
+(Rate, T<:Real)
+(T<:Real, Rate)
+(Rate, Rate)Add in the nominal space of a rate's compounding convention, the way a spread is quoted over a base rate: Periodic(0.04, 2) + 0.01 is Periodic(0.05, 2).
With two Rates, the right operand is first converted to the left operand's convention, and the result has the left operand's compounding. Rates of the same convention therefore add nominally, while for different conventions the sum depends on the order.
To combine two rates independently of their conventions, add their continuously compounded forces with Continuous(a) + b. That is the rate whose discount factor is the product of theirs: discount(Continuous(a) + b, t) ≈ discount(a, t) * discount(b, t). For two annual rates it is (1 + a)(1 + b) - 1.
Examples
julia> Periodic(0.01, 2) + Periodic(0.04, 2)
Periodic(0.05, 2)
julia> Periodic(0.04, 2) + 0.01
Periodic(0.05, 2)
julia> Periodic(0.04, 1) + Continuous(0.01) # 1% converted to annual, then added
Periodic(0.05005016708416806, 1)
julia> Continuous(0.01) + Periodic(0.04, 1) # 4% annual converted to a force, then added
Continuous(0.0492207131532813)
julia> Continuous(Periodic(0.04, 1)) + Continuous(0.01) # the same sum in either order
Continuous(0.0492207131532813)Base.:- — Method
-(Rate, T<:Real)
-(T<:Real, Rate)
-(Rate, Rate)Subtract in the nominal space of a rate's compounding convention. As with +, the right operand of Rate - Rate is first converted to the left operand's convention, so (a - b) + b recovers a.
Examples
julia> Periodic(0.04, 2) - Periodic(0.01, 2)
Periodic(0.03, 2)
julia> Periodic(0.04, 2) - 0.01
Periodic(0.03, 2)Base.convert — Method
convert(cf::Frequency,r::Rate)Returns a Rate with an equivalent discount but represented with a different compounding frequency. The stored continuous rate and its numeric type are preserved exactly. The nominal rate returned by rate can still round or overflow in the requested convention; discounting and accumulation use the preserved continuous rate.
Examples
julia> r = Rate(0.01, Periodic(12))
Periodic(0.009999999999999998, 12)
julia> convert(Periodic(1), r)
Periodic(0.010045960887182024, 1)
julia> convert(Continuous(), r)
Continuous(0.009995835646702353)Base.isless — Method
isless(a::Rate, b::Rate)Total ordering of Rates by force of interest (the continuously compounded equivalent rate). Unlike numeric < and >, this orders negative zero before positive zero and NaN after all other values.
Defining isless enables sorting and order-based functions in Base, such as sort, minimum/maximum, and extrema, to work on Rates.
Examples
julia> sort([Continuous(0.03), Periodic(0.02, 2)])
2-element Vector{Rate{Float64}}:
Periodic(0.02, 2)
Continuous(0.03)
julia> minimum([Periodic(0.05, 2), Continuous(0.03)])
Continuous(0.03)FinanceCore.accumulation — Method
accumulation(rate, t)
accumulation(rate, from, to)Accumulate rate for a time t or for an interval (from, to). If rate is not a Rate, it will be assumed to be a Periodic rate compounded once per period, i.e. Periodic(rate,1).
Examples
julia> accumulation(0.03, 10)
1.3439163793441222
julia> accumulation(Periodic(0.03, 2), 10)
1.3468550065500535
julia> accumulation(Continuous(0.03), 10)
1.3498588075760032
julia> accumulation(0.03, 5, 10)
1.1592740743FinanceCore.amount — Method
amount(x)If x is an object with an amount component (e.g. a Cashflow), will return that amount component, otherwise just x.
Examples
julia> FinanceCore.amount(Cashflow(1.,3.))
1.0
julia> FinanceCore.amount(1.)
1.0FinanceCore.compounding — Method
compounding(r::Rate)Returns the compounding frequency of the Rate.
Examples
julia> r = Continuous(0.03)
Continuous(0.03)
julia> compounding(r)
Continuous()
julia> r = Periodic(0.05, 2)
Periodic(0.05, 2)
julia> compounding(r)
Periodic(2)FinanceCore.discount — Method
discount(rate, t)
discount(rate, from, to)Discount rate for a time t or for an interval (from, to). If rate is not a Rate, it will be assumed to be a Periodic rate compounded once per period, i.e. Periodic(rate,1).
Examples
julia> discount(0.03, 10)
0.7440939148967249
julia> discount(Periodic(0.03, 2), 10)
0.7424704182237725
julia> discount(Continuous(0.03), 10)
0.7408182206817179
julia> discount(0.03, 5, 10)
0.8626087843841639FinanceCore.internal_rate_of_return — Method
internal_rate_of_return(cashflows::AbstractVector)::Rate
internal_rate_of_return(cashflows::AbstractVector, timepoints)::Rate
internal_rate_of_return(cashflows::AbstractVector{<:Cashflow})::RateCalculate the internal rate of return with given timepoints. If no timepoints given, assumes equally spaced cashflows starting at time zero (0, 1, 2, ..., n).
Returns a Periodic(rate, 1)Rate, or nothing if no root is found. Get the scalar rate by calling rate() on the result.
An empty collection of cashflows, like an all-zero one, returns nothing: there is no identifiable IRR, since every rate solves an identically zero pricing equation.
Example
julia> internal_rate_of_return([-100,110],[0,1]) # e.g. cashflows at time 0 and 1
Periodic(0.1, 1)
julia> internal_rate_of_return([-100,110]) # implied the same as above
Periodic(0.1, 1)Solver notes
First tries Newton's method (fast). If Newton does not converge, falls back to a robust root-finding search in continuous rate space over [-5, 3] (approximately [-0.993, 19.1] in periodic rate). Fallback roots are residual-validated; when multiple roots remain, returns the one nearest zero.
Derivatives
ForwardDiff derivatives of any order with respect to the cashflows or timepoints are supported, including nested dual numbers (for example ForwardDiff.hessian). Both stages solve on primal (non-dual) values. Implicit-function steps r ← r - (g(r) - g₀) / g′ for the pricing residual g, where g₀ is its primal value and g′ its primal slope at the root, then give the root its partials: the first gives dr = -(∂g/∂θ) / (∂g/∂r), and each step adds one order, so one step per dual layer suffices. The value is the primal IRR exactly. A root whose derivative vanishes (a repeated root) throws an ArgumentError, since its sensitivity is undefined.
FinanceCore.irr — Function
irr(cashflows::vector)
irr(cashflows::Vector, timepoints::Vector)An alias for internal_rate_of_return.
FinanceCore.present_value — Method
present_value(yield_model, cashflows[, timepoints=pairs(cashflows)])Discount the cashflows vector at the given yield_model, with the cashflows occurring at the times specified in timepoints. If no timepoints given, assumes that cashflows happen at the indices of the cashflows.
If your timepoints are dates, you can convert them into a floating point representation of the time interval using DayCounts.jl.
An empty collection of cashflows has a present value of exactly zero (positive zero): the value is fixed by linearity, and its numeric type follows a convention. For concrete amount, time and rate types it is the type a present value of such cashflows would have (a dual number with zero partials under ForwardDiff, a BigFloat for a BigFloat rate). When the element type says nothing about the amounts (Any[], Cashflow[], (), an empty generator), the rate or curve decides it. The zero is zero of the present value of a zero amount at time zero, so it has the valuation's type but does not depend on the rate's value (present_value(Continuous(Inf), Float64[]) is 0.0); the rate or curve is still evaluated at time zero to find that type, and can throw where that evaluation throws.
With no timepoints argument, present_value assumes cashflows occur at the vector's indices (1, 2, ..., n), while internal_rate_of_return assumes they start at time zero (0, 1, ..., n-1). Pass explicit timepoints to avoid ambiguity.
Examples
julia> present_value(0.1, [10,20],[0,1])
28.18181818181818
julia> present_value(Continuous(0.1), [10,20],[0,1])
28.096748360719193
julia> present_value(Continuous(0.1), [10,20],[1,2])
25.422989241919232
julia> present_value(Continuous(0.1), [10,20])
25.422989241919232FinanceCore.rate — Method
rate(r::Rate)Returns the nominal (untyped scalar) interest rate represented by the Rate, corresponding to its compounding frequency.
Since Rate internally stores all rates (including Periodic) as their continuously compounded equivalent for performance, rate recovers the nominal rate for the given compounding convention.
Examples
julia> r = Continuous(0.03)
Continuous(0.03)
julia> rate(r)
0.03
julia> r = Periodic(0.06, 2)
Periodic(0.06, 2)
julia> rate(r)
0.06FinanceCore.timepoint — Method
timepoint(x,t)If x is an object with a defined time component (e.g. a Cashflow), will return that time component, otherwise will return t. This is useful in handling situations where you want to handle either Cashflows or separate amount and time vectors.
Example
julia> FinanceCore.timepoint(Cashflow(1.,3.),"ignored")
3.0
julia> FinanceCore.timepoint(1.,4.)
4.0