Skip to content

Commit e3ed359

Browse files
committed
Start working on combinatorial cv
1 parent 73c8849 commit e3ed359

File tree

7 files changed

+94
-3
lines changed

7 files changed

+94
-3
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ node_modules
1616
/test/.CondaPkg/*
1717
/docs/node_modules/*
1818
/docs/package-lock.json
19+
.venv/

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ authors = ["Daniel Celis Garza <daniel.celis.garza@gmail.com>"]
77
ArgCheck = "dce04be8-c92d-5529-be00-80e4d2c0e197"
88
AverageShiftedHistograms = "77b51b56-6f8f-5c3a-9cb4-d71f9594ea6e"
99
Clustering = "aaaa29a8-35af-508c-8bc3-b662a17a0fe5"
10+
Combinatorics = "861a8166-3701-5b0c-9a16-15d98fcdc6aa"
1011
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
1112
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
1213
Distances = "b4f34e82-e78d-54a5-968a-f98e89d6e8f7"
@@ -44,6 +45,7 @@ PortfolioOptimisersPlotsExt = ["GraphRecipes", "StatsPlots"]
4445
ArgCheck = "2.5"
4546
AverageShiftedHistograms = "0.8"
4647
Clustering = "0.15"
48+
Combinatorics = "1.1.0"
4749
DataFrames = "1.8"
4850
Dates = "1.12, 1.11"
4951
Distances = "0.10"
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
struct CombinatorialCrossValidation{T1, T2, T3, T4} <: CrossValidationEstimator
2+
n_folds::T1
3+
n_test_folds::T2
4+
purged_size::T3
5+
embargo_size::T4
6+
function CombinatorialCrossValidation(n_folds::Integer, n_test_folds::Integer,
7+
purged_size::Integer, embargo_size::Integer)
8+
assert_nonempty_gt0_finite_val(n_folds, :n_folds)
9+
assert_nonempty_gt0_finite_val(n_test_folds, :n_test_folds)
10+
assert_nonempty_finite_val(purged_size, :purged_size)
11+
assert_nonempty_finite_val(embargo_size, :embargo_size)
12+
return new{typeof(n_folds), typeof(n_test_folds), typeof(purged_size),
13+
typeof(embargo_size)}(n_folds, n_test_folds, purged_size, embargo_size)
14+
end
15+
end
16+
function CombinatorialCrossValidation(; n_folds::Integer = 10, n_test_folds::Integer = 8,
17+
purged_size::Integer = 0, embargo_size::Integer = 0)
18+
return CombinatorialCrossValidation(n_folds, n_test_folds, purged_size, embargo_size)
19+
end
20+
function Base.split(ccv::CombinatorialCrossValidation, rd::ReturnsResult)
21+
#=
22+
T = size(rd.X, 1)
23+
(; n_folds, n_test_folds, purged_size, embargo_size) = ccv
24+
idx = 1:T
25+
min_fold_size = div(T, n_folds)
26+
@argcheck(purged_size + embargo_size < min_fold_size)
27+
fold_sizes = fill(min_fold_size, n_folds)
28+
fold_sizes[1:(mod(T, n_folds))] .+= one(eltype(fold_sizes))
29+
fold_indices = Vector{typeof(idx)}(undef, 0)
30+
current = one(eltype(fold_sizes))
31+
for fold_size in fold_sizes
32+
start, stop = current, current + fold_size
33+
push!(fold_indices, idx[start:(stop - 1)])
34+
current = stop
35+
end
36+
test_indices = Vector{typeof(idx)}(undef, 0)
37+
for test_fold in combinations(1:n_folds, n_test_folds)
38+
push!(test_indices, vcat(fold_indices[test_fold]...))
39+
end
40+
train_indices = Vector{Vector{eltype(T)}}(undef, 0)
41+
for test_fold in combinations(1:n_folds, n_test_folds)
42+
tmp_test_idx = Vector{typeof(idx)}(undef, 0)
43+
for j in test_fold
44+
if j == minimum(test_fold) - 1
45+
push!(tmp_test_idx, fold_indices[j][1:(end - purged_size)])
46+
elseif j == maximum(test_fold) + 1
47+
push!(tmp_test_idx,
48+
fold_indices[j][(1 + purged_size + embargo_size):end])
49+
else
50+
push!(tmp_test_idx, fold_indices[j])
51+
end
52+
end
53+
push!(train_indices,
54+
setdiff(idx, vcat(tmp_test_idx..., fold_indices[test_fold]...)))
55+
end
56+
return train_indices, test_indices
57+
=#
58+
end
59+
function n_splits(ccv::CombinatorialCrossValidation)
60+
return binomial(ccv.n_folds, ccv.n_test_folds)
61+
end
62+
function n_test_paths(ccv::CombinatorialCrossValidation)
63+
return div(n_splits(ccv) * ccv.n_test_folds, ccv.n_folds)
64+
end
65+
function average_train_size(ccv::CombinatorialCrossValidation, rd::ReturnsResult)
66+
T = size(rd.X, 1)
67+
(; n_folds, n_test_folds) = ccv
68+
return T / n_folds * (n_folds - n_test_folds)
69+
end
70+
function test_set_index(ccv::CombinatorialCrossValidation)
71+
return collect(Combinatorics.combinations(1:(ccv.n_folds), ccv.n_test_folds))
72+
end
73+
function binary_train_test_sets(ccv::CombinatorialCrossValidation)
74+
n_folds = ccv.n_folds
75+
num_splits = n_splits(ccv)
76+
type = promote_type(typeof(num_splits), typeof(n_folds))
77+
folds_train_test = zeros(type, n_folds, num_splits)
78+
test_set_idx = test_set_index(ccv)
79+
for (i, idx) in enumerate(test_set_idx)
80+
folds_train_test[idx, i] .= one(type)
81+
end
82+
return folds_train_test
83+
end
84+
85+
export CombinatorialCrossValidation, n_test_paths, average_train_size, test_set_index,
86+
binary_train_test_sets

src/20_Optimisation/02_CrossValidation/03_WalkForward.jl renamed to src/20_Optimisation/02_CrossValidation/04_WalkForward.jl

File renamed without changes.

src/20_Optimisation/02_CrossValidation/04_Validation.jl renamed to src/20_Optimisation/02_CrossValidation/05_Validation.jl

File renamed without changes.

src/PortfolioOptimisers.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module PortfolioOptimisers
33
using ArgCheck: @argcheck
44
using AverageShiftedHistograms: AverageShiftedHistograms
55
using Clustering: Clustering
6+
using Combinatorics: Combinatorics
67
using DataFrames: DataFrames
78
using Dates: Dates
89
using Distances: Distances
@@ -151,8 +152,9 @@ include("19_RiskMeasures/26_RiskMeasureTools.jl")
151152
include("20_Optimisation/01_Base_Optimisation.jl")
152153
include("20_Optimisation/02_CrossValidation/01_Base_CrossValidation.jl")
153154
include("20_Optimisation/02_CrossValidation/02_KFold.jl")
154-
include("20_Optimisation/02_CrossValidation/03_WalkForward.jl")
155-
include("20_Optimisation/02_CrossValidation/04_Validation.jl")
155+
include("20_Optimisation/02_CrossValidation/03_Combinatorial.jl")
156+
include("20_Optimisation/02_CrossValidation/04_WalkForward.jl")
157+
include("20_Optimisation/02_CrossValidation/05_Validation.jl")
156158
include("20_Optimisation/03_NaiveOptimisation.jl")
157159
include("20_Optimisation/04_Base_ClusteringOptimisation.jl")
158160
include("20_Optimisation/05_HierarchicalRiskParity.jl")

test/test_24_docs.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@
1010
push!(no_docs, sym)
1111
end
1212
end
13-
@test length(no_docs) == 395
13+
@test length(no_docs) == 398
1414
end

0 commit comments

Comments
 (0)