Skip to content

Commit

Permalink
define multiplication and division of yield objects (#136)
Browse files Browse the repository at this point in the history
  • Loading branch information
alecloudenback authored Aug 26, 2022
1 parent 12f6dba commit 0e8b9ad
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 4 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ convert(Yields.Continuous(),r) # convert monthly rate to continuous

#### Arithmetic

Adding, substracting, and comparing rates is supported.
Adding, substracting, multiplying, dividing, and comparing rates is supported.

### Curves

Expand Down
89 changes: 89 additions & 0 deletions src/RateCombination.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
## Curve Manipulations
"""
RateCombination(curve1,curve2,operation)
Creates a datastructure that will perform the given `operation` after independently calculating the effects of the two curves.
Can only be created via the public API by using the `+`, `-`, `*`, and `/` operatations on `AbstractYield` objects.
As this is double the normal operations when performing calculations, if you are using the curve in performance critical locations, you should consider transforming the inputs and
constructing a single curve object ahead of time.
"""
struct RateCombination{T,U,V} <: AbstractYieldCurve
r1::T
r2::U
Expand Down Expand Up @@ -48,6 +57,46 @@ function Base.:+(a, b::T) where {T<:AbstractYieldCurve}
return Constant(a) + b
end

"""
Yields.AbstractYieldCurve * Yields.AbstractYieldCurve
The multiplication of two yields will create a `RateCombination`. For `rate`, `discount`, and `accumulation` purposes the spot rates of the two curves will be added together. This can be useful, for example, if you wanted to after-tax a yield.
# Examples
```julia-repl
julia> m = Yields.Constant(0.01) * 0.79;
julia> accumulation(m,1)
1.0079
julia> accumulation(.01*.79,1)
1.0079
```
"""
function Base.:*(a::AbstractYieldCurve, b::AbstractYieldCurve)
return RateCombination(a, b, *)
end

function Base.:*(a::Constant, b::Constant)
a_kind = rate(a).compounding
rate_new_basis = rate(convert(a_kind, rate(b)))
return Constant(
Rate(
rate(a.rate) * rate_new_basis,
a_kind
)
)
end

function Base.:*(a::T, b) where {T<:AbstractYieldCurve}
return a * Constant(b)
end

function Base.:*(a, b::T) where {T<:AbstractYieldCurve}
return Constant(a) * b
end

"""
Yields.AbstractYieldCurve - Yields.AbstractYieldCurve
Expand All @@ -74,4 +123,44 @@ end

function Base.:-(a, b::T) where {T<:AbstractYieldCurve}
return Constant(a) - b
end

"""
Yields.AbstractYieldCurve / Yields.AbstractYieldCurve
The division of two yields will create a `RateCombination`. For `rate`, `discount`, and `accumulation` purposes the spot rates of the two curves will have the first divided by the second. This can be useful, for example, if you wanted to gross-up a yield to be pre-tax.
# Examples
```julia-repl
julia> m = Yields.Constant(0.01) / 0.79;
julia> accumulation(d,1)
1.0126582278481013
julia> accumulation(.01/.79,1)
1.0126582278481013
```
"""
function Base.:/(a::AbstractYieldCurve, b::AbstractYieldCurve)
return RateCombination(a, b, /)
end

function Base.:/(a::Constant, b::Constant)
a_kind = rate(a).compounding
rate_new_basis = rate(convert(a_kind, rate(b)))
return Constant(
Rate(
rate(a.rate) / rate_new_basis,
a_kind
)
)
end

function Base.:/(a::T, b) where {T<:AbstractYieldCurve}
return a / Constant(b)
end

function Base.:/(a, b::T) where {T<:AbstractYieldCurve}
return Constant(a) / b
end
37 changes: 34 additions & 3 deletions test/RateCombination.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
@testset "Rate Combinations" begin
riskfree_maturities = [0.5, 1.0, 1.5, 2.0]
riskfree = [5.0, 5.8, 6.4, 6.8] ./ 100 # spot rates
rf_curve = Yields.Zero(riskfree, riskfree_maturities)

@testset "base + spread" begin
riskfree_maturities = [0.5, 1.0, 1.5, 2.0]
riskfree = [5.0, 5.8, 6.4, 6.8] ./ 100 # spot rates

spread_maturities = [0.5, 1.0, 1.5, 3.0] # different maturities
spread = [1.0, 1.8, 1.4, 1.8] ./ 100 # spot spreads

rf_curve = Yields.Zero(riskfree, riskfree_maturities)
spread_curve = Yields.Zero(spread, spread_maturities)

yield = rf_curve + spread_curve
Expand All @@ -16,4 +17,34 @@
@test discount(yield, 1.0) 1 / (1 + riskfree[2] + spread[2])^1
@test discount(yield, 1.5) 1 / (1 + riskfree[3] + spread[3])^1.5
end

@testset "multiplicaiton and division" begin
@testset "multiplication" begin
factor = .79
c = rf_curve * factor
(discount(c,10)-1 * factor) discount(rf_curve,10)
(accumulation(c,10)-1 * factor) accumulation(rf_curve,10)
forward(c,5,10) * factor forward(rf_curve,5,10)
Yields.par(c,10) * factor Yields.par(rf_curve,10)

c = factor * rf_curve
(discount(c,10)-1 * factor) discount(rf_curve,10)
(accumulation(c,10)-1 * factor) accumulation(rf_curve,10)
forward(c,5,10) * factor forward(rf_curve,5,10)
Yields.par(c,10) * factor Yields.par(rf_curve,10)

@test discount(Yields.Constant(0.1) * Yields.Constant(0.1),10) discount(Yields.Constant(0.01),10)
end

@testset "division" begin
factor = .79
c = rf_curve / (factor^-1)
(discount(c,10)-1 * factor) discount(rf_curve,10)
(accumulation(c,10)-1 * factor) accumulation(rf_curve,10)
forward(c,5,10) * factor forward(rf_curve,5,10)
Yields.par(c,10) * factor Yields.par(rf_curve,10)
@test discount(Yields.Constant(0.1) / Yields.Constant(0.5),10) discount(Yields.Constant(0.2),10)
@test discount(0.1 / Yields.Constant(0.5),10) discount(Yields.Constant(0.2),10)
end
end
end

0 comments on commit 0e8b9ad

Please sign in to comment.