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.016121764134283106
 -0.0129104283203946
 -0.02144097675465071
  0.005646570828875985
  0.004094095902237762
  0.005522269626112103
 -0.021583212534241898
 -0.003678643351424106
  0.0011680686489537598
  ⋮
  0.0029311006049640483
  0.0018669432315116582
  0.003002566559219948
 -0.007903859214212586
  0.009042132179019523
 -0.0010889711920195457
 -0.002862163132448594
 -0.0044046965926032135
  0.006408102031658544)), 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.953750376702485e-8   7.025472955979495e-9   …  0.342140933687292
 3.96090721012351e-9    6.6318609167045756e-9     0.33987060133631314
 1.3407283779960735e-9  0.06512234201679544       0.3053961968537018
 2.804460203239087e-9   0.07426220781807129       0.306833429315792
 2.2879538447115884e-9  0.016657406505337105      0.29736403494631136
 7.35999272263343e-9    0.11106934895245675    …  0.23824691768713605
 1.2041452997539148e-8  0.1272772387792013        0.2352175021996021
 2.335676317517586e-9   0.0907094579912142        0.2632190360508985
 9.925301757699927e-9   0.1977684904161402        0.16077040064835238
 7.27809461926594e-10   0.0700029414940135        0.24191246600811098
 ⋮                                             ⋱  ⋮
 7.166804930182915e-9   1.0772871379598368e-9     0.26327171069493666
 8.351208424634045e-8   7.987067784579986e-9      0.16164072704616314
 4.199109772237179e-9   5.304780998172504e-10     0.16572020827786332
 4.244210041717217e-8   6.3876952005225965e-9  …  0.13471016471924654
 1.8625511275596066e-8  2.923903335514406e-9      0.06102260286574373
 6.734977243898777e-9   1.6710051770082912e-9     0.047450096895602915
 1.0917791271534785e-8  2.027643097063392e-9      0.1098637406129015
 1.176679735517942e-8   1.3728026997598458e-9     0.13622355558429472
 7.190699361866859e-9   3.4797036557484183e-9  …  0.1655853161401984), :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.0066085078924247
 1.0170827713016226
 1.0109174905842748
 0.9925290224085236
 1.0007245829599003
 1.0066711801387995
 1.0109720227895242
 0.9936284031284671
 0.9883807619717272
 ⋮
 1.1951435152478667
 1.193716378022437
 1.1972137347670935
 1.1856565903029583
 1.203484856027495
 1.2024019666072292
 1.1903401633595043
 1.1774781210752185
 1.1875447021507928), :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.006608507892424813
  0.010405498589643515
 -0.0060617295772868035
 -0.018189880328535357
  0.008257250283209845
  0.005942291495738559
  0.0042723410936744335
 -0.017155390327421503
 -0.00528129141660766
  0.0007275340087576167
  ⋮
 -0.0034607820882346114
 -0.001194113683605354
  0.002929805445453077
 -0.009653367755911546
  0.015036618419150672
 -0.0008997948040994325
 -0.01003142341970655
 -0.010805350168127646
  0.008549272292534757)), 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.0003938374594896224
  ⋮
 -0.0023130551207262086
 -0.0008485958702634104
  0.002703783384770584
 -0.007763001599731331
  0.017924529320599753
 -0.0009744422213278007
 -0.01499944501013777
 -0.014360752290415528
  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.