Skip to content

Commit 284f803

Browse files
committed
Finish interfaces gerber covariance
1 parent 2b4c836 commit 284f803

File tree

4 files changed

+230
-15
lines changed

4 files changed

+230
-15
lines changed

src/08_Moments/01_Base_Moments.jl

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ In order to implement a new covariance estimator which will work seamlessly with
5454
5555
## Factory
5656
57-
- `factory(ce::AbstractCovarianceEstimator, w::StatsBase.AbstractWeights) -> AbstractCovarianceEstimator`: Factory method for creating instances of the estimator with new observation weights.
57+
- `PortfolioOptimisers.factory(ce::AbstractCovarianceEstimator, w::StatsBase.AbstractWeights) -> AbstractCovarianceEstimator`: Factory method for creating instances of the estimator with new observation weights.
5858
5959
### Arguments
6060
@@ -86,10 +86,9 @@ julia> function MyCovarianceEstimator(;
8686
end
8787
MyCovarianceEstimator
8888
89-
julia> function factory(::MyCovarianceEstimator, w::StatsBase.AbstractWeights)
89+
julia> function PortfolioOptimisers.factory(::MyCovarianceEstimator, w::StatsBase.AbstractWeights)
9090
return MyCovarianceEstimator(; w = w)
9191
end
92-
factory (generic function with 1 method)
9392
9493
julia> function Statistics.cov(est::MyCovarianceEstimator, X::PortfolioOptimisers.MatNum;
9594
dims::Int = 1, kwargs...)
@@ -132,6 +131,10 @@ julia> cor(MyCovarianceEstimator(), [1.0 2.0; 0.3 0.7; 0.5 1.1])
132131
1.0 0.998274 0.999315
133132
0.998274 1.0 0.999764
134133
0.999315 0.999764 1.0
134+
135+
julia> PortfolioOptimisers.factory(MyCovarianceEstimator(), StatsBase.Weights([1, 2, 3]))
136+
MyCovarianceEstimator
137+
w ┴ StatsBase.Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
135138
```
136139
137140
# Related
@@ -181,7 +184,7 @@ In order to implement a new covariance estimator which will work seamlessly with
181184
182185
## Factory
183186
184-
- `factory(ve::AbstractVarianceEstimator, w::StatsBase.AbstractWeights) -> AbstractVarianceEstimator`: Factory method for creating instances of the estimator with new observation weights.
187+
- `PortfolioOptimisers.factory(ve::AbstractVarianceEstimator, w::StatsBase.AbstractWeights) -> AbstractVarianceEstimator`: Factory method for creating instances of the estimator with new observation weights.
185188
186189
### Arguments
187190
@@ -213,10 +216,9 @@ julia> function MyVarianceEstimator(;
213216
end
214217
MyVarianceEstimator
215218
216-
julia> function factory(::MyVarianceEstimator, w::StatsBase.AbstractWeights)
219+
julia> function PortfolioOptimisers.factory(::MyVarianceEstimator, w::StatsBase.AbstractWeights)
217220
return MyVarianceEstimator(; w = w)
218221
end
219-
factory (generic function with 1 method)
220222
221223
julia> function Statistics.var(est::MyVarianceEstimator, X::PortfolioOptimisers.MatNum;
222224
dims::Int = 1, kwargs...)
@@ -265,6 +267,10 @@ julia> var(MyVarianceEstimator(), [1.0 2.0; 0.3 0.7; 0.5 1.1])
265267
julia> std(MyVarianceEstimator(), [1.0 2.0; 0.3 0.7; 0.5 1.1])
266268
1×3 Matrix{Float64}:
267269
2.23607 0.761577 1.2083
270+
271+
julia> PortfolioOptimisers.factory(MyVarianceEstimator(), StatsBase.Weights([1, 2, 3]))
272+
MyVarianceEstimator
273+
w ┴ StatsBase.Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
268274
```
269275
270276
# Related
@@ -300,7 +306,7 @@ In order to implement a new expected returns estimator which will work seamlessl
300306
301307
## Factory
302308
303-
- `factory(me::AbstractExpectedReturnsEstimator, w::StatsBase.AbstractWeights) -> AbstractExpectedReturnsEstimator`: Factory method for creating instances of the estimator with new observation weights.
309+
- `PortfolioOptimisers.factory(me::AbstractExpectedReturnsEstimator, w::StatsBase.AbstractWeights) -> AbstractExpectedReturnsEstimator`: Factory method for creating instances of the estimator with new observation weights.
304310
305311
### Arguments
306312
@@ -331,10 +337,10 @@ julia> function MyExpectedReturnsEstimator(;
331337
end
332338
MyExpectedReturnsEstimator
333339
334-
julia> function factory(::MyExpectedReturnsEstimator, w::StatsBase.AbstractWeights)
340+
julia> function PortfolioOptimisers.factory(::MyExpectedReturnsEstimator,
341+
w::StatsBase.AbstractWeights)
335342
return MyExpectedReturnsEstimator(; w = w)
336343
end
337-
factory (generic function with 1 method)
338344
339345
julia> function Statistics.mean(est::MyExpectedReturnsEstimator, X::PortfolioOptimisers.MatNum;
340346
dims::Int = 1, kwargs...)
@@ -355,6 +361,10 @@ julia> mean(MyExpectedReturnsEstimator(), [1.0 2.0; 0.3 0.7; 0.5 1.1]; dims = 2)
355361
1.5
356362
0.5
357363
0.8
364+
365+
julia> PortfolioOptimisers.factory(MyExpectedReturnsEstimator(), StatsBase.Weights([1, 2, 3]))
366+
MyExpectedReturnsEstimator
367+
w ┴ StatsBase.Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
358368
```
359369
360370
# Related

src/08_Moments/03_Covariance.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ Return a new `Covariance` estimator with observation weights `w` applied to both
301301
302302
# Details
303303
304-
- Applies weights to both the expected returns estimator `ce.me` and the covariance estimator `ce.ce`.
304+
- Calls `factory(ce.me, w)` and `factory(ce.ce, w)` to propagate the weights to the mean and covariance estimators.
305305
- Preserves the moment algorithm `ce.alg` from the original estimator.
306306
- Enables weighted estimation for both mean and covariance in portfolio workflows.
307307

src/08_Moments/04_SimpleVariance.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ Return a new `SimpleVariance` estimator with the specified observation weights.
361361
362362
# Details
363363
364-
- Constructs a new `SimpleVariance` estimator with updated weights.
365364
- The mean estimator is updated using `factory(ve.me, w)` for consistency.
365+
- Sets `w` to the new weights.
366366
- The bias correction flag is preserved from the original estimator.
367367
368368
# Examples

src/08_Moments/05_GerberCovariance.jl

Lines changed: 209 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Abstract supertype for all Gerber covariance estimators in `PortfolioOptimisers.
55
66
All concrete and/or abstract types implementing Gerber covariance estimation algorithms should be subtypes of `BaseGerberCovariance`.
77
8+
# Interfaces
9+
10+
If moving away from the already established Gerber covariance algorithms, you must follow [`AbstractCovarianceEstimator`](@ref) to implement the entire chain.
11+
812
# Related
913
1014
- [`GerberCovariance`](@ref)
@@ -24,6 +28,10 @@ All concrete and/or abstract types implementing specific Gerber covariance algor
2428
2529
These types are used to specify the algorithm when constructing a [`GerberCovariance`](@ref) estimator.
2630
31+
# Interfaces
32+
33+
If moving away from the already established Gerber covariance algorithms, you must follow [`AbstractCovarianceEstimator`](@ref) to implement the entire chain. Else you can follow the instructions and examples in [`UnstandardisedGerberCovarianceAlgorithm`](@ref) and [`StandardisedGerberCovarianceAlgorithm`](@ref).
34+
2735
# Related
2836
2937
- [`BaseGerberCovariance`](@ref)
@@ -43,6 +51,104 @@ Abstract supertype for all unstandardised Gerber covariance algorithm types.
4351
4452
Concrete types implementing unstandardised Gerber covariance algorithms should subtype `UnstandardisedGerberCovarianceAlgorithm`.
4553
54+
# Interfaces
55+
56+
In order to implement a new Gerber algorithm which will work seamlessly with the library, subtype `UnstandardisedGerberCovarianceAlgorithm` with all necessary parameters as part of the struct, and implement the following methods:
57+
58+
## Gerber correlation
59+
60+
- `PortfolioOptimisers.gerber(ce::GerberCovariance{<:Any, <:Any, <:Any, <:UnstandardisedGerberCovarianceAlgorithm}, X::MatNum, sd::ArrNum) -> MatNum`: Unstandardised Gerber correlation matrix.
61+
62+
### Arguments
63+
64+
- $(arg_dict[:gerbce]). Configured with the custom `UnstandardisedGerberCovarianceAlgorithm` algorithm.
65+
- $(arg_dict[:X])
66+
- $(arg_dict[:stdarr])
67+
68+
### Returns
69+
70+
- $(ret_dict[:rho])
71+
72+
## Factory (if algorithm uses observation weights)
73+
74+
If the algorithm uses observation weights, the `factory` method will update the algorithm with the new weights.
75+
76+
- `PortfolioOptimisers.factory(alg::UnstandardisedGerberCovarianceAlgorithm, w::StatsBase.AbstractWeights) -> UnstandardisedGerberCovarianceAlgorithm`: Updates the algorithm with the new weights.
77+
78+
### Arguments
79+
80+
- $(arg_dict[:gerbalg])
81+
- $(arg_dict[:ow])
82+
83+
### Returns
84+
85+
- $(ret_dict[:algw])
86+
87+
## Examples
88+
89+
We can create a dummy unstandardised Gerber covariance algorithm as follows:
90+
91+
```jldoctest
92+
julia> struct MyUnstandardisedGerberCovarianceAlg{T} <:
93+
PortfolioOptimisers.UnstandardisedGerberCovarianceAlgorithm
94+
w::T
95+
function MyUnstandardisedGerberCovarianceAlg(w::PortfolioOptimisers.Option{<:StatsBase.AbstractWeights})
96+
if !isnothing(w)
97+
@assert(!isempty(w))
98+
end
99+
return new{typeof(w)}(w)
100+
end
101+
end
102+
103+
julia> function MyUnstandardisedGerberCovarianceAlg(;
104+
w::PortfolioOptimisers.Option{<:StatsBase.AbstractWeights} = nothing)
105+
return MyUnstandardisedGerberCovarianceAlg(w)
106+
end
107+
MyUnstandardisedGerberCovarianceAlg
108+
109+
julia> function PortfolioOptimisers.gerber(ce::GerberCovariance{<:Any, <:Any, <:Any,
110+
<:MyUnstandardisedGerberCovarianceAlg},
111+
X::PortfolioOptimisers.MatNum,
112+
sd::PortfolioOptimisers.ArrNum)
113+
rho = rand(StableRNGs.StableRNG(420), size(X, 2), size(X, 2))
114+
rho = rho * rho'
115+
return StatsBase.cov2cor!(rho)
116+
end
117+
118+
julia> function PortfolioOptimisers.factory(alg::MyUnstandardisedGerberCovarianceAlg,
119+
w::StatsBase.AbstractWeights)
120+
return MyUnstandardisedGerberCovarianceAlg(; w = w)
121+
end
122+
123+
julia> cor(GerberCovariance(; alg = MyUnstandardisedGerberCovarianceAlg()),
124+
[1.0 2.0; 0.3 0.7; 0.5 1.1])
125+
2×2 Matrix{Float64}:
126+
1.0 0.64112
127+
0.64112 1.0
128+
129+
julia> cov(GerberCovariance(; alg = MyUnstandardisedGerberCovarianceAlg()),
130+
[1.0 2.0; 0.3 0.7; 0.5 1.1])
131+
2×2 Matrix{Float64}:
132+
0.13 0.153913
133+
0.153913 0.443333
134+
135+
julia> PortfolioOptimisers.factory(GerberCovariance(; alg = MyUnstandardisedGerberCovarianceAlg()),
136+
StatsBase.Weights([1, 2, 3]))
137+
GerberCovariance
138+
ve ┼ SimpleVariance
139+
│ me ┼ SimpleExpectedReturns
140+
│ │ w ┼ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
141+
│ │ idx ┴ nothing
142+
│ w ┼ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
143+
│ corrected ┴ Bool: true
144+
pdm ┼ Posdef
145+
│ alg ┼ UnionAll: NearestCorrelationMatrix.Newton
146+
│ kwargs ┴ @NamedTuple{}: NamedTuple()
147+
t ┼ Float64: 0.5
148+
alg ┼ MyUnstandardisedGerberCovarianceAlg
149+
│ w ┴ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
150+
```
151+
46152
# Related
47153
48154
- [`GerberCovarianceAlgorithm`](@ref)
@@ -63,6 +169,103 @@ Abstract supertype for all standardised Gerber covariance algorithm types. These
63169
64170
Concrete types implementing standardised Gerber covariance algorithms should subtype `StandardisedGerberCovarianceAlgorithm`.
65171
172+
# Interfaces
173+
174+
In order to implement a new Gerber algorithm which will work seamlessly with the library, subtype `StandardisedGerberCovarianceAlgorithm` with all necessary parameters as part of the struct, and implement the following methods:
175+
176+
## Gerber correlation
177+
178+
- `PortfolioOptimisers.gerber(ce::GerberCovariance{<:Any, <:Any, <:Any, <:StandardisedGerberCovarianceAlgorithm}, X::MatNum, sd::ArrNum) -> MatNum`: Unstandardised Gerber correlation matrix.
179+
180+
### Arguments
181+
182+
- $(arg_dict[:gerbce]). Configured with the custom `StandardisedGerberCovarianceAlgorithm` algorithm.
183+
- $(arg_dict[:X])
184+
- $(arg_dict[:stdarr])
185+
186+
### Returns
187+
188+
- $(ret_dict[:rho])
189+
190+
## Factory (if algorithm uses observation weights)
191+
192+
If the algorithm uses observation weights, the `factory` method will update the algorithm with the new weights.
193+
194+
- `PortfolioOptimisers.factory(alg::StandardisedGerberCovarianceAlgorithm, w::StatsBase.AbstractWeights) -> StandardisedGerberCovarianceAlgorithm`: Updates the algorithm with the new weights.
195+
196+
### Arguments
197+
198+
- $(arg_dict[:gerbalg])
199+
- $(arg_dict[:ow])
200+
201+
### Returns
202+
203+
- $(ret_dict[:algw])
204+
205+
## Examples
206+
207+
We can create a dummy standardised Gerber covariance algorithm as follows:
208+
209+
```jldoctest
210+
julia> struct MyStandardisedGerberCovarianceAlg{T} <:
211+
PortfolioOptimisers.StandardisedGerberCovarianceAlgorithm
212+
w::T
213+
function MyStandardisedGerberCovarianceAlg(w::PortfolioOptimisers.Option{<:StatsBase.AbstractWeights})
214+
if !isnothing(w)
215+
@assert(!isempty(w))
216+
end
217+
return new{typeof(w)}(w)
218+
end
219+
end
220+
221+
julia> function MyStandardisedGerberCovarianceAlg(;
222+
w::PortfolioOptimisers.Option{<:StatsBase.AbstractWeights} = nothing)
223+
return MyStandardisedGerberCovarianceAlg(w)
224+
end
225+
MyStandardisedGerberCovarianceAlg
226+
227+
julia> function PortfolioOptimisers.gerber(ce::GerberCovariance{<:Any, <:Any, <:Any,
228+
<:MyStandardisedGerberCovarianceAlg},
229+
X::PortfolioOptimisers.MatNum)
230+
rho = rand(StableRNGs.StableRNG(420), size(X, 2), size(X, 2))
231+
rho = rho * rho'
232+
return StatsBase.cov2cor!(rho)
233+
end
234+
235+
julia> function PortfolioOptimisers.factory(alg::MyStandardisedGerberCovarianceAlg,
236+
w::StatsBase.AbstractWeights)
237+
return MyStandardisedGerberCovarianceAlg(; w = w)
238+
end
239+
240+
julia> cor(GerberCovariance(; alg = MyStandardisedGerberCovarianceAlg()),
241+
[1.0 2.0; 0.3 0.7; 0.5 1.1])
242+
2×2 Matrix{Float64}:
243+
1.0 0.64112
244+
0.64112 1.0
245+
246+
julia> cov(GerberCovariance(; alg = MyStandardisedGerberCovarianceAlg()),
247+
[1.0 2.0; 0.3 0.7; 0.5 1.1])
248+
2×2 Matrix{Float64}:
249+
0.13 0.153913
250+
0.153913 0.443333
251+
252+
julia> PortfolioOptimisers.factory(GerberCovariance(; alg = MyStandardisedGerberCovarianceAlg()),
253+
StatsBase.Weights([1, 2, 3]))
254+
GerberCovariance
255+
ve ┼ SimpleVariance
256+
│ me ┼ SimpleExpectedReturns
257+
│ │ w ┼ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
258+
│ │ idx ┴ nothing
259+
│ w ┼ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
260+
│ corrected ┴ Bool: true
261+
pdm ┼ Posdef
262+
│ alg ┼ UnionAll: NearestCorrelationMatrix.Newton
263+
│ kwargs ┴ @NamedTuple{}: NamedTuple()
264+
t ┼ Float64: 0.5
265+
alg ┼ MyStandardisedGerberCovarianceAlg
266+
│ w ┴ Weights{Int64, Int64, Vector{Int64}}: [1, 2, 3]
267+
```
268+
66269
# Related
67270
68271
- [`GerberCovarianceAlgorithm`](@ref)
@@ -498,7 +701,7 @@ The algorithm proceeds as follows:
498701
+ `D`: Entries where `X .<= -ce.t * sd`.
499702
- Compute the signed indicator matrix `UmD = U - D`.
500703
- Compute the raw Gerber2 matrix `H = UmD' * UmD`.
501-
- Normalize: `rho = H ⊘ (h * h')`, where `h = sqrt.(LinearAlgebra.diag(H))`.
704+
- Normalise `rho = H ⊘ (h * h')`, where `h = sqrt.(LinearAlgebra.diag(H))`.
502705
- The result is projected to the nearest positive definite matrix using `posdef!`.
503706
504707
# Related
@@ -554,7 +757,7 @@ The algorithm proceeds as follows:
554757
+ `D`: Entries where `X .<= -ce.t`.
555758
- Compute the signed indicator matrix `UmD = U - D`.
556759
- Compute the raw Gerber2 matrix `H = UmD' * UmD`.
557-
- Normalize: `rho = H ⊘ (h * h')`, where `h = sqrt.(LinearAlgebra.diag(H))`.
760+
- Normalise `rho = H ⊘ (h * h')`, where `h = sqrt.(LinearAlgebra.diag(H))`.
558761
- The result is projected to the nearest positive definite matrix using `posdef!`.
559762
560763
# Related
@@ -744,7 +947,8 @@ Return a new `GerberCovariance` estimator with the specified observation weights
744947
745948
# Details
746949
747-
- Applies `factory(ce.ve, w)` to update the variance estimator.
950+
- Calls `factory(ce.alg, w)` to update the algorithm (current algorithms do not use weights, this for future proofing).
951+
- Calls `factory(ce.ve, w)` to update the variance estimator.
748952
- Preserves the other fields of the original estimator.
749953
750954
# Examples
@@ -786,7 +990,8 @@ GerberCovariance
786990
- [`factory`](@ref)
787991
"""
788992
function factory(ce::GerberCovariance, w::StatsBase.AbstractWeights)
789-
return GerberCovariance(; alg = ce.alg, ve = factory(ce.ve, w), pdm = ce.pdm, t = ce.t)
993+
return GerberCovariance(; alg = factory(ce.alg, w), ve = factory(ce.ve, w),
994+
pdm = ce.pdm, t = ce.t)
790995
end
791996

792997
export GerberCovariance, Gerber0, Gerber1, Gerber2, StandardisedGerber0,

0 commit comments

Comments
 (0)