Robust Portfolio Optimization

Robust Return as Risk Constraint

using HiGHS
using Clarabel
using Distributions
using PortfolioOpt
using PortfolioOpt.TestUtils

Read 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 omitted

Backtest parameters

DEFAULT_SOLVER = optimizer_with_attributes(
    HiGHS.Optimizer, "presolve" => "on", "time_limit" => 60.0, "log_to_console" => false
)

Clarabel_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-01

Backtest Soyster's robust return

backtest_results = Dict()

backtest_results["Soyster"], _ = sequential_backtest_market(
    VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
    numD, numA = size(past_returns)
    returns = values(past_returns)
    k_back = 30
    Σ, r̄ = mean_variance(returns[(end - k_back):end, :])

    d = MvNormal(r̄, Σ)
    R = -0.015 / market_budget(market)

    formulation = PortfolioFormulation(MAX_SENSE,
        ObjectiveTerm(ExpectedReturn(d)),
        RiskConstraint(ExpectedReturn(BudgetSet(d; Δ=maximum(abs.(returns); dims=1)'[:] .- r̄, Γ=numA * 1.0)), 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}:
 -0.0  -0.0  -0.0   0.0  -0.0                   0.22133782399067825
  0.0  -0.0  -0.0  -0.0  -0.0                   0.22274374461691798
 -0.0  -0.0  -0.0   0.0  -0.0                   0.22346785096665636
 -0.0  -0.0  -0.0   0.0  -0.0                   0.22264233125936214
  0.0  -0.0   0.0   0.0  -0.0                   0.2130308591611971
 -0.0  -0.0   0.0   0.0  -0.0                   0.21026542724706543
 -0.0  -0.0   0.0   0.0  -0.0                   0.21181605677601587
 -0.0  -0.0   0.0   0.0  -0.0                   0.21732069450387714
  0.0  -0.0   0.0   0.0   0.0                   0.2040926851602658
  0.0  -0.0   0.0   0.0  -0.0                   0.2024300359954988
  ⋮                                             ⋮
 -0.0   0.0   0.0  -0.0  -0.0                   0.20601915037031057
 -0.0   0.0  -0.0  -0.0   0.1944466634604701   -0.0
 -0.0   0.0  -0.0  -0.0  -0.0                   0.19850920482854967
 -0.0   0.0  -0.0  -0.0   0.1941799010326254   -0.0
 -0.0   0.0  -0.0  -0.0   0.19434675809892202  -0.0
 -0.0   0.0  -0.0  -0.0   0.198113869042182    -0.0
 -0.0   0.0   0.0  -0.0   0.19472377696208812  -0.0
 -0.0   0.0   0.0  -0.0   0.1878552264024443   -0.0
  0.0   0.0   0.0  -0.0  -0.0                   0.1903899042825381), :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.0032107027958757
 1.0103767213894495
 1.0076680201656112
 0.996545660058877
 1.002048302177432
 1.007153667767064
 1.011802834948276
 1.0009741405485983
 0.9982061565726453
 ⋮
 1.0904873347476276
 1.0887699451783006
 1.087794469970298
 1.0848792364691553
 1.0915120118449635
 1.0911311457077468
 1.0853800742854316
 1.0786065977131964
 1.0848175607136306), :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.0032107027958756342
  0.007143084272927611
 -0.0026808824535400387
 -0.011037722627047565
  0.0055217160026865535
  0.005094929634168496
  0.00461614481484187
 -0.010702376021936383
 -0.002765290194645818
  0.0011152730505045718
  ⋮
 -0.006302792592161171
 -0.0015748826369675444
 -0.0008959424461730876
 -0.0026799488153512454
  0.006113837515588495
 -0.0003489344442236808
 -0.005270742609574128
 -0.006240649457927837
  0.005758320979680924)), nothing)

Backtest Bertsimas's robust return

backtest_results["Bertsimas"], _ = sequential_backtest_market(
    VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
    numD, numA = size(past_returns)
    returns = values(past_returns)
    k_back = 30
    Σ, r̄ = mean_variance(returns[(end - k_back):end, :])

    d = MvNormal(r̄, Σ)
    R = -0.015 / market_budget(market)

    formulation = PortfolioFormulation(MAX_SENSE,
        ObjectiveTerm(ExpectedReturn(d)),
        RiskConstraint(ExpectedReturn(BudgetSet(d; Δ=maximum(abs.(returns); dims=1)'[:] .- r̄, Γ=numA * 0.3)), 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}:
 0.20289308736653008   0.24486952446614163  …  0.12726584082925055
 0.1953388446189664    0.24063740619103535     0.12537474874411006
 0.1988578503457958    0.13702293602214888     0.1268202707862655
 0.19187430733948374   0.13507535643019292     0.12469450606375979
 0.0                  -0.0                     0.2154258801732391
 0.18592588582365166   0.1317305186810252   …  0.1189860566133338
 0.18584228910267342   0.1323870870545527      0.1195078623397213
 0.19412986482017278   0.13552701359555935     0.12379524488343199
 0.0                   0.13060766191114231     0.11618447426663105
 0.0                  -0.0                     0.20716602453458405
 ⋮                                          ⋱  ⋮
 0.10960021189313257   0.0                     0.11094174387120354
 0.11196184769442043   0.0                     0.10966622870818174
 0.1115496686720318    0.0                     0.10970771038082248
 0.10985249024803737   0.0                  …  0.10644013245886837
 0.10734653247722736   0.0                     0.10316144334132374
 0.10918578734767424   0.0                     0.10521880851067232
 0.10834929035351762   0.0                     0.10547950413651319
 0.10650457785573118   0.0                     0.10278488574177262
 0.0                   0.0                  …  0.10091695075143108), :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.004043437302107
 1.0202303887788664
 1.0070587774742485
 0.9854664536358562
 0.9910309597657923
 0.9950883355571601
 1.000583481647906
 0.9789876757052475
 0.9753863292008882
 ⋮
 1.1539393095922201
 1.1560936487758386
 1.1595648969049797
 1.1503998592100997
 1.160801926795803
 1.1595378469378816
 1.1562190604616973
 1.1511262663057789
 1.1585028008715885), :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.004043437302106927
  0.0161217641342831
 -0.0129104283203946
 -0.02144097675465071
  0.005646570828875985
  0.004094095902237762
  0.005522269626112104
 -0.0215832125342419
 -0.003678643351424106
  0.0011680686489537598
  ⋮
  0.0029311006049640483
  0.0018669432315116582
  0.003002566559219948
 -0.007903859214212586
  0.009042132179019523
 -0.0010889711920195457
 -0.0028621631324485934
 -0.0044046965926032135
  0.006408102031658545)), nothing)

Backtest BenTal's robust return

backtest_results["Ben-Tal"], _ = sequential_backtest_market(
    VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
    numD, numA = size(past_returns)
    returns = values(past_returns)
    k_back = 30
    Σ, r̄ = mean_variance(returns[(end - k_back):end, :])

    d = MvNormal(r̄, Σ)
    R = -0.015 / market_budget(market)

    formulation = PortfolioFormulation(MAX_SENSE,
        ObjectiveTerm(ExpectedReturn(d)),
        RiskConstraint(ExpectedReturn(EllipticalSet(d, 2.0)), GreaterThan(R)),
    )

    pointers = change_bids!(market, formulation, Clarabel_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}:
 1.9541462119732542e-8  7.026271875037349e-9   …  0.3421409337612164
 3.960907287295588e-9   6.631861050397249e-9      0.33987060133774805
 1.3407284661925974e-9  0.0651223420169545        0.30539619685501673
 2.8044601648203983e-9  0.07426220781836933       0.30683342931704155
 2.2879538375386375e-9  0.016657406505454712      0.29736403494754365
 7.359992809269768e-9   0.11106934895290851    …  0.23824691768812378
 1.20414532692836e-8    0.12727723877955963       0.23521750220076085
 2.335676226760204e-9   0.09070945799150089       0.2632190360520086
 9.925444602241768e-9   0.19776849032364086       0.1607704007202544
 7.278094817890565e-10  0.0700029414935197        0.24191246600946725
 ⋮                                             ⋱  ⋮
 7.166804746713069e-9   1.0772871265218471e-9     0.26327171070345645
 8.351208466593402e-8   7.987067845360762e-9      0.16164072705185561
 4.199109293234975e-9   5.304780445799851e-10     0.16572020828368958
 4.244210090684159e-8   6.387695297318574e-9   …  0.13471016472399364
 1.8625994438598678e-8  2.9239834049197745e-9     0.06102260286685821
 6.7349776468423934e-9  1.671005278463826e-9      0.04745009689715881
 1.0917791535062312e-8  2.0276431518644726e-9     0.10986374061657284
 1.1766796422277999e-8  1.3728025933803183e-9     0.13622355558909593
 7.190542657991347e-9   3.4796319574088654e-9  …  0.1655853162626334), :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.0066085078882934
 1.017082771297536
 1.010917490580165
 0.992529022404339
 1.0007245829557507
 1.0066711801346744
 1.0109720227854153
 0.9936284031242869
 0.9883807619680404
 ⋮
 1.1951435152056402
 1.1937163779801603
 1.1972137347249414
 1.1856565902604
 1.2034848559857616
 1.2024019665654577
 1.1903401633173114
 1.1774781210325707
 1.18754470209964), :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.006608507888293395
  0.010405498589730828
 -0.00606172957733401
 -0.01818988032868323
  0.008257250283279784
  0.005942291495787712
  0.004272341093708048
 -0.017155390327561727
 -0.005281291416133291
  0.0007275340087722406
  ⋮
 -0.003460782088446757
 -0.0011941136836896608
  0.0029298054456611622
 -0.009653367756590845
  0.015036618420386082
 -0.0008997948041621483
 -0.01003142342040554
 -0.01080535016889293
  0.008549272285621322)), nothing)

Backtest Betina's robust return

backtest_results["Betina"], _ = sequential_backtest_market(
    VolumeMarketHistory(returns_series), date_range,
) do market, past_returns, ext
    numD, numA = size(past_returns)
    returns = values(past_returns)

    k_back = 30
    j_robust = 25
    R = -0.015 / market_budget(market)

    Σ, r̄ = mean_variance(returns[(end - k_back):end, :])
    d = MvNormal(r̄, Σ)

    formulation = PortfolioFormulation(MAX_SENSE,
        ObjectiveTerm(ExpectedReturn(d)),
        RiskConstraint(ConditionalExpectedReturn(DeterministicSamples(returns'[:, (end - j_robust):end]); α=0.0), 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}:
 0.0                    0.0                  0.0  …  0.2540909090909085
 0.0                    0.0                  0.0     0.253157816077229
 0.0                    0.6137185446847665   0.0     0.0
 0.0                    0.6099549849010794   0.0     0.0
 0.0                    0.21204802641048387  0.0     0.16758912938636167
 0.0                    0.21032620965256546  0.0  …  0.16622831610124383
 0.0                    0.4731002693601723   0.0     0.0
 0.0                    0.21007840953985046  0.0     0.16603247082102715
 0.0                    0.43394409169277687  0.0     0.0
 0.0                    0.16439121677363955  0.0     0.19043039900320258
 ⋮                                                ⋱  ⋮
 0.0                    0.0                  0.0     0.24588303002828832
 0.0                    0.0                  0.0     0.24645308960922777
 0.0                    0.0                  0.0     0.24666240630857042
 0.0                    0.0                  0.0  …  0.0
 0.0                    0.0                  0.0     0.0
 0.0                    0.0                  0.0     0.0
 0.0                    0.0                  0.0     0.0
 0.0028475918799927055  0.0                  0.0     0.2121355167856915
 0.0                    0.0                  0.0  …  0.18627628419120565), :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.003685815544383
 1.0118303029248839
 1.0180735240318015
 0.9989636811544662
 1.0071416082402296
 1.011995852066605
 1.008329592310504
 0.9927416936197444
 0.9860116099319938
 ⋮
 1.170063782201865
 1.1690708709083437
 1.172231785304725
 1.1631317480801486
 1.1839803372023316
 1.1828266167725399
 1.1650848739777329
 1.1483533787052287
 1.1598597607037724), :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.0036858155443830498
  0.008114578540779065
  0.0061702254704868965
 -0.018770592129393616
  0.008186410817570983
  0.004819822541993041
 -0.0036228011692084187
 -0.01545913043674663
 -0.006779289850526293
 -0.0003938374594896225
  ⋮
 -0.002313055120726208
 -0.0008485958702634102
  0.002703783384770584
 -0.007763001599731331
  0.017924529320599753
 -0.0009744422213278007
 -0.01499944501013777
 -0.01436075229041553
  0.010019896498686912)), 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
plt

This page was generated using Literate.jl.