The source files can be found in user_guide/.

Data and priors

The first stage turns prices into a prior, which holds the expected returns vector and the covariance matrix that most optimisers use. Two calls cover the common path, prices_to_returns and prior. For the other moment estimators and the priors that take views, see the moments and priors examples.

using PortfolioOptimisers, CSV, TimeSeries, DataFrames, PrettyTables, LinearAlgebra,      StatsPlots, GraphRecipesresfmt = (v, i, j) -> begin    return if j == 1        v    else        isa(v, AbstractFloat) ? "$(round(v*100, digits=3)) %" : v    endend;

1. Prices to returns

Market data usually arrives as prices, and an optimiser needs returns. prices_to_returns converts a TimeArray of prices into a ReturnsResult that holds the asset names nx and the return matrix X. Before it converts its inputs, it checks that their names, dates and shapes match.

A real price table has gaps. An asset that lists or delists inside the sample has no price before or after it trades, a halted asset has no quotes, and two exchanges close on different holidays. price_ingestion reads the first and the last quote of each asset, its listing span. The conversion keeps every gap in the returns, and it returns an AssetPanel on rd.pnl that states which assets have data on each date. To fill a gap inside the listing span, use PriceGapFill. To drop the assets or the dates with too much missing data, use MissingDataFilter. The page Data preprocessing and the ingestion layer covers these steps in depth.

X = TimeArray(CSV.File(joinpath(@__DIR__, "../examples/SP500.csv.gz")); timestamp = :Date)[(end - 252):end]rd = prices_to_returns(X)
ReturnsResult
    nx ┼ 20-element Vector{String}
     X ┼ 252×20 Matrix{Float64}
    nf ┼ nothing
     F ┼ nothing
    nb ┼ nothing
     B ┼ nothing
    ne ┼ nothing
     E ┼ nothing
    ts ┼ 252-element Vector{Date}
    iv ┼ nothing
  ivpa ┼ nothing
   pnl ┼ AssetPanel
       │     pf ┼ Vector{AbstractPanelField}: AbstractPanelField[]
       │   amsk ┼ 252×20 AllTrueMask
       │   emsk ┴ 252×20 AllTrueMask

On a price table, prices_to_returns(X) is the same call as prices_to_returns(price_ingestion(PriceIngestion(), X)). Write the two calls when you have more than one table. price_ingestion takes factor prices as F, benchmark prices as B, and implied volatilities as iv. By default it aligns them on the dates of the asset prices. The PriceIngestion estimator also sets how to collapse the prices to a lower frequency. The conversion then puts the factor returns in rd.F and the benchmark returns in rd.B, on the same ReturnsResult as the asset returns. The point-in-time universe runs these two calls on a price table with gaps.

2. Returns to a prior

prior applies a prior estimator to a ReturnsResult and returns the moments. The JuMP, clustering and meta-optimisers use EmpiricalPrior by default. It computes the sample mean and the sample covariance, and then repairs the covariance to the nearest positive definite matrix if it needs to. We compute it and print the mean and the volatility of each asset.

pr = prior(EmpiricalPrior(), rd)pretty_table(DataFrame("Asset" => rd.nx, "Expected return" => pr.mu,                       "Volatility" => sqrt.(diag(pr.sigma))); formatters = [resfmt],             title = "Empirical prior: per-asset mean and volatility")
Empirical prior: per-asset mean and volatility
┌────────┬─────────────────┬────────────┐
│  Asset │ Expected return │ Volatility │
│ String │         Float64 │    Float64 │
├────────┼─────────────────┼────────────┤
│   AAPL │        -0.113 % │    2.237 % │
│    AMD │        -0.281 % │     3.84 % │
│    BAC │        -0.093 % │     2.04 % │
│    BBY │        -0.028 % │     2.85 % │
│    CVX │         0.195 % │     2.07 % │
│     GE │        -0.034 % │    2.192 % │
│     HD │        -0.071 % │    1.968 % │
│    JNJ │         0.031 % │    1.096 % │
│    JPM │        -0.042 % │    1.877 % │
│     KO │          0.05 % │    1.237 % │
│    LLY │         0.131 % │    1.714 % │
│    MRK │         0.167 % │    1.254 % │
│   MSFT │        -0.121 % │    2.214 % │
│    PEP │         0.039 % │    1.223 % │
│    PFE │        -0.026 % │    1.698 % │
│     PG │        -0.008 % │    1.382 % │
│    RRC │         0.182 % │    3.957 % │
│    UNH │         0.036 % │    1.533 % │
│    WMT │         0.016 % │    1.681 % │
│    XOM │         0.264 % │    2.207 % │
└────────┴─────────────────┴────────────┘

3. Other priors

You call every prior estimator with prior(pe, rd), and you change the prior by changing its first argument. The common alternatives are these:

A cross-sectional prior needs more input than the others. It does not read F. It reads an AssetPanel of data for each asset on each date, such as a market capitalisation, a book equity or an industry label, which the returns hold in rd.pnl. You give it a list of pairs. Each pair names a factor and the estimator that computes the exposures of the assets to that factor from the panel. An industry label gives one factor for each industry, and one pair then gives many factors. The page Cross-sectional factor model, end to end fits one, and Cross-sectional factor model through a Pipeline gets the same weights through a Pipeline.

EmpiricalPrior holds a covariance estimator in its field ce, and you can change it for another, such as a denoised covariance or the Gerber covariance. The page Covariance Estimation compares them. A moment estimator can also use only the last part of the sample, or weight the recent returns more than the old ones. Use this when the recent returns describe the market better than the full sample does, as the page Windowed Estimators shows. RegimeAdjustedExpWeightedVariance and RegimeAdjustedExpWeightedCovariance multiply an exponentially weighted estimate by a factor that measures how far the recent standardised returns are from their usual size.

4. A first look at the data

plot_prior shows a prior in one figure: the expected returns, the volatility of each asset and the correlation matrix. Look at it before you optimise, to find an asset with an expected return or a volatility that you do not expect.

plot_prior(pr, rd)
Example block output

This page was generated using Literate.jl.