-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathPortfolioOptimization.jl
More file actions
118 lines (98 loc) · 3.09 KB
/
PortfolioOptimization.jl
File metadata and controls
118 lines (98 loc) · 3.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
module PortfolioOptimization
using ..Utils
using DocStringExtensions: TYPEDEF, TYPEDFIELDS, TYPEDSIGNATURES
using Distributions: Uniform, Bernoulli
using Flux: Chain, Dense
using Ipopt: Ipopt
using JuMP: @variable, @objective, @constraint, optimize!, value, Model, set_silent
using LinearAlgebra: I
using Random: AbstractRNG, MersenneTwister
"""
$TYPEDEF
Benchmark problem for the portfolio optimization problem.
Data is generated using the process described in: <https://arxiv.org/abs/2307.13565>.
# Fields
$TYPEDFIELDS
"""
struct PortfolioOptimizationBenchmark <: AbstractBenchmark
"number of assets"
d::Int
"size of feature vectors"
p::Int
"hypermarameter for data generation"
deg::Int
"another hyperparameter, should be positive"
ν::Float32
"covariance matrix"
Σ::Matrix{Float32}
"maximum variance of portfolio"
γ::Float32
"useful for dataset generation"
L::Matrix{Float32}
"useful for dataset generation"
f::Vector{Float32}
end
"""
$TYPEDSIGNATURES
Constructor for [`PortfolioOptimizationBenchmark`](@ref).
"""
function PortfolioOptimizationBenchmark(;
d::Int=50, p::Int=5, deg::Int=1, ν::Float32=1.0f0, seed=0
)
rng = MersenneTwister(seed)
f = randn(rng, Float32, 4)
L = Float32.(rand(rng, Uniform(-0.0025ν, 0.0025ν), d, 4))
Σ = L * L' + (0.01f0ν)^2 * I
e = ones(d) ./ d
γ = 2.25e' * Σ * e
return PortfolioOptimizationBenchmark(d, p, deg, ν, Σ, γ, L, f)
end
function Base.show(io::IO, bench::PortfolioOptimizationBenchmark)
(; d, p, deg, ν) = bench
return print(io, "PortfolioOptimizationBenchmark(d=$d, p=$p, deg=$deg, ν=$ν)")
end
"""
$TYPEDSIGNATURES
Create a function solving the MIQP formulation of the portfolio optimization problem.
"""
function Utils.generate_maximizer(bench::PortfolioOptimizationBenchmark)
(; d, Σ, γ) = bench
function portfolio_maximizer(θ)
model = Model(Ipopt.Optimizer)
set_silent(model)
@variable(model, x[1:d] >= 0)
@objective(model, Max, θ' * x)
@constraint(model, sum(x) <= 1)
@constraint(model, x' * Σ * x <= γ)
optimize!(model)
return value.(x)
end
return portfolio_maximizer
end
"""
$TYPEDSIGNATURES
Generate a labeled sample for the portfolio optimization problem.
"""
function Utils.generate_sample(
bench::PortfolioOptimizationBenchmark, rng::AbstractRNG; type::Type=Float32
)
(; d, p, deg, ν, L, f) = bench
features = randn(rng, type, p)
B = rand(rng, Bernoulli(0.5), d, p)
c̄ = (0.05 / type(sqrt(p)) .* B * features .+ 0.1^(1 / deg)) .^ deg
θ_true = c̄ .+ L * f .+ 0.01 * ν * randn(rng, type, d)
maximizer = Utils.generate_maximizer(bench)
y_true = maximizer(θ_true)
return DataSample(; x=features, θ=θ_true, y=y_true)
end
"""
$TYPEDSIGNATURES
Initialize a linear model for `bench` using `Flux`.
"""
function Utils.generate_statistical_model(bench::PortfolioOptimizationBenchmark)
(; p, d) = bench
return Dense(p, d)
end
export PortfolioOptimizationBenchmark
export generate_dataset, generate_maximizer, generate_statistical_model
end