Financial Math Submodule
Provides a set of common routines in financial maths.
Quickstart
cfs = [5, 5, 105]
times = [1, 2, 3]
discount_rate = 0.03
present_value(discount_rate, cfs, times) # 105.65
duration(Macaulay(), discount_rate, cfs, times) # 2.86
duration(discount_rate, cfs, times) # 2.78
convexity(discount_rate, cfs, times) # 10.62
API
Exported API
ActuaryUtilities.FinancialMath.KeyRate
— TypeKeyRate(timepoints,shift=0.001)
A convenience constructor for KeyRateZero
.
Extended Help
KeyRateZero
is chosen as the default constructor because it has more attractive properties than KeyRatePar
:
- rates after the key
timepoint
remain unaffected by theshift
- e.g. this causes a 6-year zero coupon bond would have a negative duration if the 5-year par rate was used
ActuaryUtilities.FinancialMath.KeyRatePar
— TypeKeyRatePar(timepoint,shift=0.001) <: KeyRateDuration
Shift the par curve by the given amount at the given timepoint. Use in conjunction with duration
to calculate the key rate duration.
Unlike other duration statistics which are computed using analytic derivatives, KeyRateDuration
s are computed via a shift-and-compute the yield curve approach.
KeyRatePar
is more commonly reported (than KeyRateZero
) in the fixed income markets, even though the latter has more analytically attractive properties. See the discussion of KeyRateDuration in the FinanceModels.jl docs.
ActuaryUtilities.FinancialMath.KeyRateZero
— TypeKeyRateZero(timepoint,shift=0.001) <: KeyRateDuration
Shift the par curve by the given amount at the given timepoint. Use in conjunction with duration
to calculate the key rate duration.
Unlike other duration statistics which are computed using analytic derivatives, KeyRateDuration
is computed via a shift-and-compute the yield curve approach.
KeyRateZero
is less commonly reported (than KeyRatePar
) in the fixed income markets, even though the latter has more analytically attractive properties. See the discussion of KeyRateDuration in the FinanceModels.jl docs.
ActuaryUtilities.FinancialMath.breakeven
— Functionbreakeven(yield, cashflows::Vector)
breakeven(yield, cashflows::Vector,times::Vector)
Calculate the time when the accumulated cashflows breakeven given the yield.
Assumptions:
- cashflows occur at the end of the period
- cashflows evenly spaced with the first one occuring at time zero if
times
not given
Returns nothing
if cashflow stream never breaks even.
julia> breakeven(0.10, [-10,1,2,3,4,8])
5
julia> breakeven(0.10, [-10,15,2,3,4,8])
1
julia> breakeven(0.10, [-10,-15,2,3,4,8]) # returns the `nothing` value
ActuaryUtilities.FinancialMath.convexity
— Methodconvexity(yield,cfs,times)
convexity(yield,valuation_function)
Calculates the convexity. - yield
should be a fixed effective yield (e.g. 0.05
). - times
may be omitted and it will assume cfs
are evenly spaced beginning at the end of the first period.
Examples
Using vectors of cashflows and times
julia> times = 1:5
julia> cfs = [0,0,0,0,100]
julia> duration(0.03,cfs,times)
4.854368932038834
julia> duration(Macaulay(),0.03,cfs,times)
5.0
julia> duration(Modified(),0.03,cfs,times)
4.854368932038835
julia> convexity(0.03,cfs,times)
28.277877274012614
Using any given value function:
julia> lump_sum_value(amount,years,i) = amount / (1 + i ) ^ years
julia> my_lump_sum_value(i) = lump_sum_value(100,5,i)
julia> duration(0.03,my_lump_sum_value)
4.854368932038835
julia> convexity(0.03,my_lump_sum_value)
28.277877274012617
ActuaryUtilities.FinancialMath.moic
— Methodmoic(cashflows<:AbstractArray)
The multiple on invested capital ("moic") is the un-discounted sum of distributions divided by the sum of the contributions. The function assumes that negative numbers in the array represent contributions and positive numbers represent distributions.
Examples
julia> moic([-10,20,30])
5.0
ActuaryUtilities.FinancialMath.present_values
— Functionpresent_values(interest, cashflows, timepoints)
Efficiently calculate a vector representing the present value of the given cashflows at each period prior to the given timepoint.
Examples
julia> present_values(0.00, [1,1,1])
[3,2,1]
julia> present_values(ForwardYield([0.1,0.2]), [10,20],[0,1]) # after `using FinanceModels`
2-element Vector{Float64}:
28.18181818181818
18.18181818181818
ActuaryUtilities.FinancialMath.price
— Methodprice(...)
The absolute value of the present_value(...)
.
Extended help
Using price
can be helpful if the directionality of the value doesn't matter. For example, in the common usage, duration is more interested in the change in price than present value, so price
is used there.
ActuaryUtilities.FinancialMath.spread
— Functionspread(curve1,curve2,cashflows)
Return the solved-for constant spread to add to curve1
in order to equate the discounted cashflows
with curve2
Examples
spread(0.04, 0.05, cfs)
Rate{Float64, Periodic}(0.010000000000000009, Periodic(1))
ActuaryUtilities.duration
— Methodduration(keyrate::KeyRateDuration,curve,cashflows)
duration(keyrate::KeyRateDuration,curve,cashflows,timepoints)
duration(keyrate::KeyRateDuration,curve,cashflows,timepoints,krd_points)
Calculate the key rate duration by shifting the zero (not par) curve by the kwarg shift
at the timepoint specified by a KeyRateDuration(time).
The approach is to carve up the curve into krd_points
(default is the unit steps between 1
and the last timepoint of the casfhlows). The zero rate corresponding to the timepoint within the KeyRateDuration
is shifted by shift
(specified by the KeyRateZero
or KeyRatePar
constructors. A new curve is created from the shifted rates. This means that the "width" of the shifted section is ± 1 time period, unless specific points are specified via krd_points
.
The curve
may be any FinanceModels.jl curve (e.g. does not have to be a curve constructed via FinanceModels.Zero(...)
).
!!! Experimental: Due to the paucity of examples in the literature, this feature does not have unit tests like the rest of JuliaActuary functionality. Additionally, the API may change in a future major/minor version update.
Examples
julia> riskfree_maturities = [0.5, 1.0, 1.5, 2.0];
julia> riskfree = [0.05, 0.058, 0.064,0.068];
julia> rf_curve = FinanceModels.Zero(riskfree,riskfree_maturities);
julia> cfs = [10,10,10,10,10];
julia> duration(KeyRate(1),rf_curve,cfs)
8.932800152336995
Extended Help
Key Rate Duration is not a well specified topic in the literature and in practice. The reference below suggest that shocking the par curve is more common in practice, but that the zero curve produces more consistent results. Future versions may support shifting the par curve.
References:
- Quant Finance Stack Exchange: To compute key rate duration, shall I use par curve or zero curve?
- (Financial Exam Help 123](http://www.financialexamhelp123.com/key-rate-duration/)
ActuaryUtilities.duration
— Methodduration(Macaulay(),interest_rate,cfs,times)
duration(Modified(),interest_rate,cfs,times)
duration(DV01(),interest_rate,cfs,times)
duration(interest_rate,cfs,times) # Modified Duration
duration(interest_rate,valuation_function) # Modified Duration
Calculates the Macaulay, Modified, or DV01 duration. times
may be ommitted and the valuation will assume evenly spaced cashflows starting at the end of the first period.
Note that the calculated duration will depend on the periodicity convention of the interest_rate
: a Periodic
yield (or yield model with that convention) will be a slightly different computed duration than a Continous
which follows from the present value differing according to the periodicity.
When not given Modified()
or Macaulay()
as an argument, will default to Modified()
.
- Modified duration: the relative change per point of yield change.
- Macaulay: the cashflow-weighted average time.
- DV01: the absolute change per basis point (hundredth of a percentage point).
Examples
Using vectors of cashflows and times
julia> times = 1:5;
julia> cfs = [0,0,0,0,100];
julia> duration(0.03,cfs,times)
4.854368932038835
julia> duration(Periodic(0.03,1),cfs,times)
4.854368932038835
julia> duration(Continuous(0.03),cfs,times)
5.0
julia> duration(Macaulay(),0.03,cfs,times)
5.0
julia> duration(Modified(),0.03,cfs,times)
4.854368932038835
julia> convexity(0.03,cfs,times)
28.277877274012614
Using any given value function:
julia> lump_sum_value(amount,years,i) = amount / (1 + i ) ^ years
julia> my_lump_sum_value(i) = lump_sum_value(100,5,i)
julia> duration(0.03,my_lump_sum_value)
4.854368932038835
julia> convexity(0.03,my_lump_sum_value)
28.277877274012617