Skip to content

Commit 076e996

Browse files
migrate from BSplineKit to DataInterpolations, provide PolynomialSpline (#178)
* migrate from BSplineKit to DataInterpolations, provide `PolynomialSpline` * fix compat * DataInterpolations 4.6 and above requires Julia 1.10
1 parent b58fce9 commit 076e996

File tree

8 files changed

+56
-24
lines changed

8 files changed

+56
-24
lines changed

.github/workflows/ci.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
matrix:
1111
version:
12-
- '1.9'
12+
- '1.10'
1313
- '1'
1414
os:
1515
- ubuntu-latest

Project.toml

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
name = "FinanceModels"
22
uuid = "77f2ae65-bdde-421f-ae9d-22f1af19dd76"
33
authors = ["Alec Loudenback <alecloudenback@gmail.com> and contributors"]
4-
version = "4.7.0"
4+
version = "4.8.0"
55

66
[deps]
77
AccessibleOptimization = "d88a00a0-4a21-4fe4-a515-e2123c37b885"
88
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
9-
BSplineKit = "093aae92-e908-43d7-9660-e50ee39d5a0a"
9+
DataInterpolations = "82cc6244-b520-54b8-b5a6-8a565e85f1d0"
1010
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1111
FinanceCore = "b9b1ffdd-6612-4b69-8227-7663be06e089"
1212
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
@@ -29,11 +29,11 @@ FinanceModelsMakieCoreExt = "MakieCore"
2929
[compat]
3030
AccessibleOptimization = "^0.1.1"
3131
Accessors = "^0.1"
32-
BSplineKit = "^0.16, 0.17"
32+
DataInterpolations = "^4.7.1"
3333
Dates = "^1.6"
3434
FinanceCore = "^2.1"
3535
IntervalSets = "^0.7"
36-
LinearAlgebra = "^1.6"
36+
LinearAlgebra = "1"
3737
MakieCore = "0.7"
3838
Optimization = "^3.15"
3939
OptimizationMetaheuristics = "^0.1.2, 0.2"
@@ -43,4 +43,4 @@ SpecialFunctions = "2"
4343
StaticArrays = "^1.6"
4444
Transducers = "^0.4"
4545
UnicodePlots = "^3.6"
46-
julia = "1.9"
46+
julia = "1.10"

src/Contract.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ coupon_times(b::AbstractBond) = coupon_times(b.maturity, b.frequency.frequency)
319319

320320
for op = (:ZCBPrice, :ZCBYield, :ParYield, :ParSwapYield, :CMTYield, :ForwardYield)
321321
eval(quote
322-
$op(x::Vector; kwargs...) = $op.(x, eachindex(x); kwargs...)
322+
$op(x::Vector; kwargs...) = $op.(x, float.(eachindex(x)); kwargs...)
323323
end)
324324
end
325325

src/FinanceModels.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ using AccessibleOptimization
1111
using Accessors
1212
using LinearAlgebra
1313
using Transducers
14-
import BSplineKit
14+
import DataInterpolations
1515
import UnicodePlots
1616
using Transducers: @next, complete, __foldl__, asfoldable
1717
import SpecialFunctions

src/fit.jl

+1-1
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,7 @@ function fit(mod0, quotes, method::F=Fit.Loss(x -> x^2);
281281

282282
end
283283

284-
function fit(mod0::Spline.BSpline, quotes, method::Fit.Bootstrap)
284+
function fit(mod0::T, quotes, method::Fit.Bootstrap) where {T<:Spline.SplineCurve}
285285
discount_vector = [0.0]
286286
times = [maturity(quotes[1])]
287287

src/model/Spline.jl

+11-6
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,28 @@ Spline is a module which offers various degree splines used for fitting or boots
33
44
Available methods:
55
6-
- `Spline.BSpline(n)` where n is the nth order. A spline function of order n is a piecewise polynomial function of degree n − 1. This means that, e.g., cubic polynomial is a fourth degree B-Spline.
6+
- `Spline.BSpline(n)` where n is the nth order. A nth-order B-Spline is analagous to an (n-1)th order polynomial spline. That is, a 3rd/4th order BSpline is very similar to a quadratic/cubic spline respectively. BSplines are global in that a change in one point affects the entire spline (though the spline still passes through the other given points still).
7+
- `Spline.PolynomialSpline(n)` where n is the nth order.
78
89
This object is not a fitted spline itself, rather it is a placeholder object which will be a spline representing the data only after using within [`fit`](@ref FinanceModels.fit-Union{Tuple{F}, Tuple{Any, Any}, Tuple{Any, Any, F}} where F<:FinanceModels.Fit.Loss).
910
1011
and convienience methods which create a `Spline.BSpline` object of the appropriate order.
1112
12-
- `Spline.Linear()`
13-
- `Spline.Quadratic()`
14-
- `Spline.Cubic()`
13+
- `Spline.Linear()` equals `BSpline(2)`
14+
- `Spline.Quadratic()` equals `BSpline(3)`
15+
- `Spline.Cubic()` equals `BSpline(4)`
1516
"""
1617
module Spline
1718
import ..FinanceCore
18-
import ..BSplineKit
1919
import ..AbstractModel
2020

21+
abstract type SplineCurve end
2122

22-
struct BSpline
23+
struct PolynomialSpline <: SplineCurve
24+
order::Int
25+
end
26+
27+
struct BSpline <: SplineCurve
2328
order::Int
2429
end
2530

src/model/Yield.jl

+23-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Yield
22
import ..AbstractModel
33
import ..FinanceCore
44
import ..Spline as Sp
5-
import ..BSplineKit
5+
import ..DataInterpolations
66
import UnicodePlots
77
import ..Bond: coupon_times
88

@@ -33,8 +33,8 @@ Constant() = Constant(0.0)
3333
FinanceCore.discount(c::Constant, t) = FinanceCore.discount(c.rate, t)
3434

3535
# used as the object which gets optmized before finally returning a completed spline
36-
struct IntermediateYieldCurve{U,V} <: AbstractYieldModel
37-
b::Sp.BSpline
36+
struct IntermediateYieldCurve{T<:Sp.SplineCurve,U,V} <: AbstractYieldModel
37+
b::T
3838
xs::Vector{U}
3939
ys::Vector{V} # here, ys are the discount factors
4040
end
@@ -60,12 +60,28 @@ function FinanceCore.discount(c::Spline, time)
6060
end
6161

6262
function Spline(b::Sp.BSpline, xs, ys)
63-
order = min(length(xs), b.order) # in case the length of xs is less than the spline order
64-
int = BSplineKit.interpolate(xs, ys, BSplineKit.BSplineOrder(order))
65-
return Spline(BSplineKit.extrapolate(int, BSplineKit.Smooth()))
63+
order = min(length(xs) - 1, b.order) # in case the length of xs is less than the spline order
64+
xs = float.(xs)
65+
knot_type = if length(xs) < 3
66+
:Uniform
67+
else
68+
:Average
69+
end
70+
71+
return Spline(DataInterpolations.BSplineInterpolation(ys, xs, order, :Uniform, knot_type; extrapolate=true))
72+
end
73+
74+
function Spline(b::Sp.PolynomialSpline, xs, ys)
75+
order = min(length(xs) - 1, b.order) # in case the length of xs is less than the spline order
76+
if order == 1
77+
return Spline(DataInterpolations.LinearInterpolation(ys, xs; extrapolate=true))
78+
elseif order == 2
79+
return Spline(DataInterpolations.QuadraticSpline(ys, xs; extrapolate=true))
80+
else
81+
return Spline(DataInterpolations.CubicSpline(ys, xs; extrapolate=true))
82+
end
6683
end
6784

68-
6985
include("Yield/SmithWilson.jl")
7086
include("Yield/NelsonSiegelSvensson.jl")
7187

test/Yield.jl

+13-2
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,19 @@ end
111111
maturity = [0.5, 1.0, 1.5, 2.0]
112112
zero = [5.0, 5.8, 6.4, 6.8] ./ 100
113113
zs = ZCBYield.(zero, maturity)
114-
@testset "$i" for (i, curve) in enumerate([fit(Spline.Cubic(), zs, Fit.Bootstrap()), fit(Spline.Linear(), zs, Fit.Bootstrap()), fit(Spline.Quadratic(), zs, Fit.Bootstrap()), fit(Spline.BSpline(5), zs, Fit.Bootstrap())])
115-
114+
variants = [
115+
Spline.Cubic(),
116+
Spline.Linear(),
117+
Spline.Quadratic(),
118+
Spline.BSpline(5),
119+
Spline.BSpline(3),
120+
Spline.BSpline(1),
121+
Spline.PolynomialSpline(1),
122+
Spline.PolynomialSpline(2),
123+
Spline.PolynomialSpline(3),
124+
]
125+
@testset "Constructor $i" for (i, m) in enumerate(variants)
126+
curve = fit(m, zs, Fit.Bootstrap())
116127
@test discount(curve, 1) 1 / 1.058
117128
@test discount(curve, 1.5) 1 / 1.064^1.5
118129
@test discount(curve, 2) 1 / 1.068^2

0 commit comments

Comments
 (0)