Skip to content

Commit

Permalink
little bit of progress
Browse files Browse the repository at this point in the history
  • Loading branch information
alecloudenback committed Nov 21, 2023
1 parent 2d35be2 commit 946bca6
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 51 deletions.
24 changes: 20 additions & 4 deletions src/fit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ __default_optic(m::MyModel) = OptArgs([
"""
__default_optic(m::Yield.Constant) = OptArgs(@optic(_.rate.value) => -1.0 .. 1.0)
__default_optic(m::Yield.MonotoneConvex) = OptArgs(@optic(_.rates[*]) => -1.0 .. 1.0)

Check warning on line 133 in src/fit.jl

View check run for this annotation

Codecov / codecov/patch

src/fit.jl#L133

Added line #L133 was not covered by tests
__default_optic(m::Yield.IntermediateYieldCurve) = OptArgs(@optic(_.ys[end]) => 0.0 .. 1.0)
__default_optic(m::Yield.NelsonSiegel) = OptArgs([
@optic(_.τ₁) => 0.0 .. 100.0
Expand Down Expand Up @@ -222,7 +223,7 @@ The default solver is `ECA()` from Metahueristics.jl. This is a stochastic globa
## Defining the variables
An arbitrarily complex model may be the object we intend to fit - how does `fit` know what free variables are able to be solved for within the given model?
`variables` is a singlular or vector optic argument. What does this mean?
`variables` is a singular or vector optic argument. What does this mean?
- An optic (or "lens") is a way to define an accessor to a given object. Example:
```julia-repl
Expand All @@ -237,7 +238,7 @@ julia> lens(obj)
"AA"
```
An optic argument is a singular or vector of lenses with an optional range of acceptable parameters. For example, we might have a model as follows where we want
`fit` to optize parameters `a` and `b`:
`fit` to optimize parameters `a` and `b`:
```julia
struct MyModel <:FinanceModels.AbstractModel
Expand All @@ -252,7 +253,7 @@ __default_optic(m::MyModel) = OptArgs([
```
In this way, fit know which arbitrary parameters in a given object may be modified. Technically, we are not modifying the immutable `MyModel`, but instead efficiently creating a new instance. This is enabled by [AccessibleOptimization.jl](https://gitlab.com/aplavin/AccessibleOptimization.jl).
Note that not all opitmization algorithms want a bounded interval. In that case, simply leave off the paired range. The prior example would then become:
Note that not all optimization algorithms want a bounded interval. In that case, simply leave off the paired range. The prior example would then become:
```julia
__default_optic(m::MyModel) = OptArgs([
Expand All @@ -265,7 +266,7 @@ __default_optic(m::MyModel) = OptArgs([
## Additional Examples
See the tutorials in the package documentation for FinanceModels.jl or the docstrings of FinanceModels.jl's avaiable model types.
See the tutorials in the package documentation for FinanceModels.jl or the docstrings of FinanceModels.jl's available model types.
"""
function fit(mod0, quotes, method::F=Fit.Loss(x -> x^2);
variables=__default_optic(mod0),
Expand All @@ -281,6 +282,21 @@ function fit(mod0, quotes, method::F=Fit.Loss(x -> x^2);

end

function fit(mod0::Yield.MonotoneConvexUnInit, quotes, method;
variables=__default_optic(mod0),
optimizer=__default_optim(mod0)
)
# use maturities as the times
# fit a vector of rates to the times
times = [maturity(q.contract) for q in quotes]
if !iszero(first(times))
pushfirst!(times, zero(eltype(times)))
end
mc = mod0(times)
rates = fit(mc, quotes, method)
end


function fit(mod0::Spline.BSpline, quotes, method::Fit.Bootstrap)
discount_vector = [0.0]
times = [maturity(quotes[1])]
Expand Down
1 change: 1 addition & 0 deletions src/model/Yield.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ end

include("Yield/SmithWilson.jl")
include("Yield/NelsonSiegelSvensson.jl")
include("Yield/MonotoneConvex.jl")

## Generic and Fallbacks
"""
Expand Down
128 changes: 81 additions & 47 deletions src/model/Yield/MonotoneConvex.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,30 @@
"""
An unfit Monotone Convex Yield Curve Model will simply have
"""
struct MonotoneConvex{T,U} <: AbstractYieldModel
f::Vector{T}
fᵈ::Vector{T}
rates::Vector{T}
times::Vector{U}
# inner constructor ensures f consistency with rates at construction
function MonotoneConvex(rates::Vector{T}, times::Vector{U}) where {T,U}
f, fᵈ = __monotone_convex_fs(rates, times)
new{T,U}(f, fᵈ, rates, times)

Check warning on line 14 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L12-L14

Added lines #L12 - L14 were not covered by tests
end
end


struct MonotoneConvexUnInit
end

MonotoneConvex() = MonotoneConvexUnInit()
function (m::MonotoneConvexUnInit)(times)
rates = zeros(length(times))
MonotoneConvex(rates, times)

Check warning on line 25 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L22-L25

Added lines #L22 - L25 were not covered by tests
end


function __issector1(g0, g1)
a = (g0 > 0) && (g1 >= -2 * g0) && (-0.5 * g0 >= g1)
Expand All @@ -13,13 +40,11 @@ end

# Hagan West - WILMOTT magazine pgs 75-81
function g(x, f⁻, f, fᵈ)
@show x, f⁻, f, fᵈ
@show g0 = f⁻ - fᵈ
@show g1 = f - fᵈ
g0 = f⁻ - fᵈ
g1 = f - fᵈ
A = -2 * g0
B = -2 * g1
if sign(g0) == sign(g1)

Check warning on line 47 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L42-L47

Added lines #L42 - L47 were not covered by tests
@show "(iv)"
# sector (iv)
η = g1 / (g1 + g0)
α = -g0 * g1 / (g1 + g0)

Check warning on line 50 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L49-L50

Added lines #L49 - L50 were not covered by tests
Expand All @@ -32,11 +57,9 @@ function g(x, f⁻, f, fᵈ)


elseif __issector1(g0, g1)

Check warning on line 59 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L59

Added line #L59 was not covered by tests
@show "(i)"
# sector (i)
g0 * (1 - 4 * x + 3 * x^2) + g1 * (-2 * x + 3 * x^2)
elseif __issector2(g0, g1)

Check warning on line 62 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L61-L62

Added lines #L61 - L62 were not covered by tests
@show "(ii)"
# sector (ii)
η = (g1 + 2 * g0) / (g1 - g0)
if x < η
Expand All @@ -45,7 +68,6 @@ function g(x, f⁻, f, fᵈ)
return g0 + (g1 - g0) * ((x - η) / (1 - η))^2

Check warning on line 68 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L68

Added line #L68 was not covered by tests
end
else
@show "(iii)"
# sector (iii)
η = 3 * g1 / (g1 - g0)
if x > η
Expand All @@ -58,13 +80,11 @@ function g(x, f⁻, f, fᵈ)
end

function g_rate(x, f⁻, f, fᵈ)
@show x, f⁻, f, fᵈ
@show g0 = f⁻ - fᵈ
@show g1 = f - fᵈ
g0 = f⁻ - fᵈ
g1 = f - fᵈ
A = -2 * g0
B = -2 * g1
if sign(g0) == sign(g1)

Check warning on line 87 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L82-L87

Added lines #L82 - L87 were not covered by tests
@show "(iv)"
# sector (iv)
η = g1 / (g1 + g0)
α = -g0 * g1 / (g1 + g0)

Check warning on line 90 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L89-L90

Added lines #L89 - L90 were not covered by tests
Expand All @@ -77,11 +97,9 @@ function g_rate(x, f⁻, f, fᵈ)


elseif __issector1(g0, g1)

Check warning on line 99 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L99

Added line #L99 was not covered by tests
@show "(i)"
# sector (i)
g0 * (x - 2 * x^2 + x^3) + g1 * (-x^2 + x^3)
elseif __issector2(g0, g1)

Check warning on line 102 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L101-L102

Added lines #L101 - L102 were not covered by tests
@show "(ii)"
# sector (ii)
η = (g1 + 2 * g0) / (g1 - g0)
if x < η
Expand All @@ -90,7 +108,6 @@ function g_rate(x, f⁻, f, fᵈ)
return g0 * x + ((g1 - g0) * (x - η)^3 / (1 - η)^2) / 3

Check warning on line 108 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L108

Added line #L108 was not covered by tests
end
else
@show "(iii)"
# sector (iii)
η = 3 * g1 / (g1 - g0)
if x > η
Expand All @@ -107,14 +124,8 @@ function forward(t, rates, times)
t, i_time, rates, times = __monotone_convex_init(t, rates, times)
f, fᵈ = __monotone_convex_fs(rates, times)

Check warning on line 125 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L124-L125

Added lines #L124 - L125 were not covered by tests

@show x = (t - times[i_time]) / (times[i_time+1] - times[i_time])
@show fᵈ, f
@show fᵈ[i_time+1], g(x, f[i_time], f[i_time+1], fᵈ[i_time+1])
x = (t - times[i_time]) / (times[i_time+1] - times[i_time])
return fᵈ[i_time+1] + g(x, f[i_time], f[i_time+1], fᵈ[i_time+1])

Check warning on line 128 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L127-L128

Added lines #L127 - L128 were not covered by tests




end

"""
Expand All @@ -123,22 +134,22 @@ end
function __monotone_convex_init(t, rates, times)

Check warning on line 134 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L134

Added line #L134 was not covered by tests
# the array indexing in the paper and psuedo-VBA is messy
t = min(t, last(times))

Check warning on line 136 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L136

Added line #L136 was not covered by tests
times = collect(times)
rates = collect(rates)
# times = collect(times)
# rates = collect(rates)
i_time = findfirst(x -> x > t, times)
if i_time == nothing
i_time = lastindex(times)

Check warning on line 141 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L139-L141

Added lines #L139 - L141 were not covered by tests
end
if !iszero(first(times))
pushfirst!(times, zero(eltype(times)))
pushfirst!(rates, first(rates))
# if !iszero(first(times))
# pushfirst!(times, zero(eltype(times)))
# pushfirst!(rates, first(rates))

end
# end

return t, i_time, rates, times

Check warning on line 149 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L149

Added line #L149 was not covered by tests
end
"""
returns a pair of vectors (f and fᵈ) used in Monotone Convext Yield Curve fitting
returns a pair of vectors (f and fᵈ) used in Monotone Convex Yield Curve fitting
"""
function __monotone_convex_fs(rates, times)

Check warning on line 154 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L154

Added line #L154 was not covered by tests
# step 1
Expand Down Expand Up @@ -176,31 +187,54 @@ function myzero(t, rates, times)
t, i_time, rates, times = __monotone_convex_init(t, rates, times)
f, fᵈ = __monotone_convex_fs(rates, times)
x = (t - times[i_time]) / (times[i_time+1] - times[i_time])
@show G = g_rate(x, f[i_time], f[i_time+1], fᵈ[i_time+1])
G = g_rate(x, f[i_time], f[i_time+1], fᵈ[i_time+1])
return 1 / t * (times[i_time] * rates[i_time] + (t - times[i_time]) * fᵈ[i_time+1] + (times[i_time+1] - times[i_time]) * G)

Check warning on line 191 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L187-L191

Added lines #L187 - L191 were not covered by tests




end

function Base.zero(mc::MonotoneConvex, t)
lt = last(times)

Check warning on line 199 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L198-L199

Added lines #L198 - L199 were not covered by tests
# if the time is greater than the last input time then extrapolate using the forwards
if t > lt
r = myzero(lt, rates, times)
return r * lt / t + forward(lt, rates, times) * (1 - lt / t)

Check warning on line 203 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L201-L203

Added lines #L201 - L203 were not covered by tests
end

t, i_time, rates, times = __monotone_convex_init(t, rates, times)
f, fᵈ = mc.f, mc.fᵈ
x = (t - times[i_time]) / (times[i_time+1] - times[i_time])
G = g_rate(x, f[i_time], f[i_time+1], fᵈ[i_time+1])
return 1 / t * (times[i_time] * rates[i_time] + (t - times[i_time]) * fᵈ[i_time+1] + (times[i_time+1] - times[i_time]) * G)

Check warning on line 210 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L206-L210

Added lines #L206 - L210 were not covered by tests

end

function FinanceCore.discount(mc::MonotoneConvex, t)
r = zero(mc, t)
return exp(-r * t)

Check warning on line 216 in src/model/Yield/MonotoneConvex.jl

View check run for this annotation

Codecov / codecov/patch

src/model/Yield/MonotoneConvex.jl#L214-L216

Added lines #L214 - L216 were not covered by tests
end

times = 1:5
rates = [0.03, 0.04, 0.047, 0.06, 0.06]
forward(5.19, rates, times)
myzero(1, rates, times)


using Test
@test forward(0.5, rates, times) 0.02875
@test forward(1, rates, times) 0.04
@test forward(2, rates, times) 0.0555
@test forward(2.5, rates, times) 0.0571254591368226
@test forward(5, rates, times) 0.05025
@test forward(5.2, rates, times) 0.05025

@test myzero(0.5, rates, times) 0.02625
@test myzero(1, rates, times) 0.03
@test myzero(2, rates, times) 0.04
@test myzero(2.5, rates, times) 0.0431375956535047
@test myzero(5, rates, times) 0.06
@test myzero(5.2, rates, times) 0.059625
# forward(5.19, rates, times)
# myzero(1, rates, times)


# using Test
# @test forward(0.5, rates, times) ≈ 0.02875
# @test forward(1, rates, times) ≈ 0.04
# @test forward(2, rates, times) ≈ 0.0555
# @test forward(2.5, rates, times) ≈ 0.0571254591368226
# @test forward(5, rates, times) ≈ 0.05025
# @test forward(5.2, rates, times) ≈ 0.05025

# @test myzero(0.5, rates, times) ≈ 0.02625
# @test myzero(1, rates, times) ≈ 0.03
# @test myzero(2, rates, times) ≈ 0.04
# @test myzero(2.5, rates, times) ≈ 0.0431375956535047
# @test myzero(5, rates, times) ≈ 0.06
# @test myzero(5.2, rates, times) ≈ 0.059625


0 comments on commit 946bca6

Please sign in to comment.