Stochastic Portfolio Optimization
Empirical Forecast
using Clarabel
using Distributions
using PortfolioOpt
using PortfolioOpt.TestUtilsRead Prices
prices = get_test_data();
numD, numA = size(prices) # A: Assets D: Days(316, 6)Calculating returns
returns_series = percentchange(prices);315×6 TimeSeries.TimeArray{Float64, 2, Dates.Date, Matrix{Float64}} 2009-09-02 to 2010-12-01
┌────────────┬──────────────┬──────────────┬─────────────┬─────────────┬────────
│ │ AAPL │ BA │ DELL │ CAT │ EBAY ⋯
├────────────┼──────────────┼──────────────┼─────────────┼─────────────┼────────
│ 2009-09-02 │ -0.000725953 │ -0.00758663 │ 0.00920447 │ -0.00752737 │ -0.0 ⋯
│ 2009-09-03 │ 0.00829398 │ 0.00123967 │ -0.00651466 │ 0.0351643 │ 0.00 ⋯
│ 2009-09-04 │ 0.0225758 │ 0.0142385 │ 0.0288525 │ 0.0237567 │ 0.0 ⋯
│ 2009-09-08 │ 0.0153837 │ 0.00712106 │ 0.0172084 │ 0.0186511 │ -0.0 ⋯
│ 2009-09-09 │ -0.010351 │ 0.0208081 │ -0.00250627 │ 0.0306579 │ 0.0 ⋯
│ 2009-09-10 │ 0.0082973 │ -0.000791609 │ 0.040201 │ 0.00578393 │ 0.0 ⋯
│ 2009-09-11 │ -0.00231803 │ 0.0170331 │ 0.00241546 │ -0.0032861 │ 0.00 ⋯
│ 2009-09-14 │ 0.00906134 │ -0.00740019 │ -0.0126506 │ 0.00494539 │ 0.0 ⋯
│ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋮ │ ⋱
│ 2010-11-22 │ 0.0216151 │ 0.00691933 │ 0.00431655 │ 0.00035727 │ 0.0 ⋯
│ 2010-11-23 │ -0.0147753 │ -0.0067156 │ -0.0100287 │ -0.0163095 │ -0.0 ⋯
│ 2010-11-24 │ 0.0196612 │ 0.0284591 │ 0.00434153 │ 0.0249304 │ 0.0 ⋯
│ 2010-11-26 │ 0.000635324 │ -0.00932579 │ -0.0165706 │ -0.00661235 │ -0.00 ⋯
│ 2010-11-29 │ 0.00593651 │ -0.00679012 │ -0.00586081 │ -0.00546773 │ -0.0 ⋯
│ 2010-11-30 │ -0.0180516 │ -0.00916718 │ -0.0257922 │ 0.0111151 │ -0.0 ⋯
│ 2010-12-01 │ 0.0168729 │ 0.0305786 │ 0.0143722 │ 0.0336879 │ 0.00 ⋯
└────────────┴──────────────┴──────────────┴─────────────┴─────────────┴────────
2 columns and 300 rows omittedBacktest Parameters
DEFAULT_SOLVER = optimizer_with_attributes(
Clarabel.Optimizer, "verbose" => false, "max_iter" => 900000
)
date_range = timestamp(returns_series)[100:end];216-element Vector{Dates.Date}:
2010-01-26
2010-01-27
2010-01-28
2010-01-29
2010-02-01
2010-02-02
2010-02-03
2010-02-04
2010-02-05
2010-02-08
⋮
2010-11-18
2010-11-19
2010-11-22
2010-11-23
2010-11-24
2010-11-26
2010-11-29
2010-11-30
2010-12-01Backtest Markowitz
backtest_results = Dict()
backtest_results["EP_markowitz_limit_var"], _ = sequential_backtest_market(
VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
max_std = 0.003 / market_budget(market)
k_back = 60
numD, numA = size(past_returns)
returns = values(past_returns)
Σ, r̄ = mean_variance(returns[(end - k_back):end, :])
d = MvNormal(r̄, Σ)
formulation = PortfolioFormulation(MAX_SENSE,
ObjectiveTerm(ExpectedReturn(d)),
RiskConstraint(SqrtVariance(d), LessThan(max_std)),
)
pointers = change_bids!(market, formulation, DEFAULT_SOLVER)
return pointers
end(Dict{Symbol, PortfolioOpt.StateRecorder}(:decisions => PortfolioOpt.DecisionRecorder{Float64}(2-dimensional DenseAxisArray{Float64,2,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-17"), Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01")]
Dimension 2, 1:6
And data, a 216×6 Matrix{Float64}:
4.366487243347157e-9 0.051471020223320806 … 0.09208739135995175
4.441139431008122e-9 0.04853283532126676 0.09428049411322846
6.367552375655185e-10 0.04873958041716492 0.09381704711308525
2.915443424032201e-9 0.05865046456267631 0.09189344890754525
6.131542967966947e-10 0.05285985758323522 0.09340665473536365
6.590893487710306e-9 0.062049387240225753 … 0.09314985686413274
1.5796212196434964e-8 0.05073527936527546 0.10098358086439349
5.359486058766986e-9 0.03243789954844179 0.11231226878151233
7.579223811107884e-9 0.010195261313021021 0.11832076277406893
2.2967358698637423e-9 0.01177899892950056 0.11885749020320997
⋮ ⋱ ⋮
0.045805598816261214 2.4194958490360625e-9 0.08561076820499741
0.07810358917302992 4.367850929777909e-9 0.06716540010482776
0.05930492030645432 1.6537257663198853e-9 0.06994728486515317
0.07327135807767554 4.861880664745549e-9 … 0.06490995836719536
0.08294183128949724 2.436610704955135e-9 0.0573923420178588
0.057010295436011986 1.0782484341413406e-9 0.05727990299946496
0.06407299722634069 1.0938608232641959e-9 0.06610396546443205
0.08804453329668337 1.6202809077256874e-9 0.07205767109839953
0.0625744186951413 9.249391862189811e-10 … 0.07308891273022117), :wealth => PortfolioOpt.WealthRecorder{Float64}(1-dimensional DenseAxisArray{Float64,1,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01"), Dates.Date("2010-12-02")]
And data, a 217-element Vector{Float64}:
1.0
1.0012734543074289
1.0078555333543902
1.007127412488936
1.000699251964002
1.0040927205015404
1.0065763226117619
1.0083997081174805
1.001673919254561
0.9999110883893652
⋮
1.0663364277932357
1.0659911475049875
1.0677488464595803
1.0630550140671615
1.0688436502128842
1.0689730632812784
1.0668057848416723
1.0627353564706306
1.0684316559137899), :returns => PortfolioOpt.ReturnsRecorder{Float64}(1-dimensional DenseAxisArray{Float64,1,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-17"), Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01")]
And data, a 216-element Vector{Float64}:
0.0012734543074288207
0.006573707730535992
-0.0007224456694013908
-0.006382668612949351
0.003391097306087074
0.0024734788526112068
0.0018114726769923922
-0.006669764785509027
-0.0017598849598756618
0.0005508460929086674
⋮
-0.000484752456811761
-0.00032380051853123884
0.0016488870087775034
-0.0043960079263794345
0.005445283705098127
0.00012107764158812885
-0.0020274397120479907
-0.0038155289640146167
0.005360035693248103)), nothing)Backtest Sampled CVAR
backtest_results["EP_limit_cvar"], _ = sequential_backtest_market(
VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
numD, numA = size(past_returns)
returns = values(past_returns)
R = -0.001 / market_budget(market)
d = DeterministicSamples(returns'[:,:])
formulation = PortfolioFormulation(MAX_SENSE,
ObjectiveTerm(ExpectedReturn(d)),
RiskConstraint(ConditionalExpectedReturn(d), GreaterThan(R)),
)
pointers = change_bids!(market, formulation, DEFAULT_SOLVER)
return pointers
end(Dict{Symbol, PortfolioOpt.StateRecorder}(:decisions => PortfolioOpt.DecisionRecorder{Float64}(2-dimensional DenseAxisArray{Float64,2,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-17"), Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01")]
Dimension 2, 1:6
And data, a 216×6 Matrix{Float64}:
3.1567043375203975e-11 2.0112058401432956e-11 … 0.9999999998779389
3.3638301295518935e-11 1.2675768621411662e-11 1.014505892897507
5.5845932992526734e-12 3.7674903581490166e-12 1.0471441522818419
1.923572700533939e-11 4.874912602088713e-11 1.034451495793822
1.7908761428534175e-10 6.143609385664203e-10 0.9827742499064525
7.875438988219784e-11 2.395695535618274e-10 … 1.0081595642879428
1.825762934029435e-11 4.6717182752391856e-11 1.0326382591307754
1.194714956217017e-10 3.206334945717336e-10 1.0553037156506275
7.959272995485972e-12 1.8308766988467514e-11 1.0027198548810867
1.5806596233966034e-11 2.0961218559998176e-11 0.9891205801562638
⋮ ⋱ ⋮
6.639853347942747e-9 4.1557515250147524e-11 1.3177332336785483
6.73125313805476e-8 3.313747630742742e-10 1.2734927253821577
1.743638508189528e-9 2.3692948865558733e-11 1.2861329216613564
3.7877172175396645e-8 1.8823681401579372e-10 … 1.279812817942401
4.0967318027527036e-8 1.7408779486698868e-10 1.2403124215155066
1.32990594891597e-8 6.121540928107645e-11 1.260062648139794
3.340029358693889e-8 1.522324298895472e-10 1.2719127475153937
1.186329625879939e-8 2.8344982853943345e-10 1.2687526901441781
6.263432064719233e-10 7.320076996334804e-12 … 1.259272655095799), :wealth => PortfolioOpt.WealthRecorder{Float64}(1-dimensional DenseAxisArray{Float64,1,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01"), Dates.Date("2010-12-02")]
And data, a 217-element Vector{Float64}:
1.0
1.0145058930179014
1.0471441523062617
1.0344514959145228
0.9827742520378777
1.008159564795834
1.0326382592549634
1.0553037170936874
1.0027198549365555
0.9891205802321482
⋮
1.2734927993050285
1.2861329252906988
1.2798128618325164
1.2403124655643292
1.2600626642388575
1.271912783176925
1.2687527516872124
1.2592726571728394
1.3003530700705106), :returns => PortfolioOpt.ReturnsRecorder{Float64}(1-dimensional DenseAxisArray{Float64,1,...} with index sets:
Dimension 1, [Dates.Date("2010-01-26"), Dates.Date("2010-01-27"), Dates.Date("2010-01-28"), Dates.Date("2010-01-29"), Dates.Date("2010-02-01"), Dates.Date("2010-02-02"), Dates.Date("2010-02-03"), Dates.Date("2010-02-04"), Dates.Date("2010-02-05"), Dates.Date("2010-02-08") … Dates.Date("2010-11-17"), Dates.Date("2010-11-18"), Dates.Date("2010-11-19"), Dates.Date("2010-11-22"), Dates.Date("2010-11-23"), Dates.Date("2010-11-24"), Dates.Date("2010-11-26"), Dates.Date("2010-11-29"), Dates.Date("2010-11-30"), Dates.Date("2010-12-01")]
And data, a 216-element Vector{Float64}:
0.014505893017901422
0.0321715817650597
-0.012121212121353335
-0.04995617878725092
0.025830258276829287
0.02428057552981391
0.02194907813611017
-0.04982817866116127
-0.013562386979230262
0.005499541703913747
⋮
-0.03357314105296417
0.00992555748455604
-0.0049140048698729085
-0.03086419698238386
0.01592356702271976
0.009404388586678487
-0.0024844718376204884
-0.0074719794709930945
0.032622333744543965)), nothing)Plot
using Plots
using Plots.PlotMeasures
plt = plot(;title="Culmulative Wealth",
xlabel="Time",
ylabel="Wealth",
legend=:outertopright,
left_margin=10mm
);
for (strategy_name, recorders) in backtest_results
plot!(plt,
axes(get_records(recorders[:wealth]), 1), get_records(recorders[:wealth]).data;
label=strategy_name,
)
end
pltThis page was generated using Literate.jl.