Portfolio Allocation for Bayesian Dummies
This post is about the Black-Litterman (BL) model for asset allocation and the basis of my talk at the Dublin Data Science Meet-up. The original BL paper (Black and Litterman (1991)) is over 30 years old and builds on the ideas of modern portfolio theory by Harry Markowitz (Markowitz (1952)). A good introduction to the BL model is (Idzorek (2005)) or (Maggiar (2009)).
I am not sure how much the model is used by investment professionals, as many of the underlying assumptions may not hold true in the real world. However, the model uses some great ideas, including Bayes’ rule, to combine different pieces of information to form a more robust view that are worth exploring and are probably applicable in other fields. For the avoidance of doubt, this post should not be regarded as financial advice.
Motivation
What’s the problem the BL model is addressing?
According to modern portfolio theory (MPT), investors are trying to derive asset weights for their portfolio such that returns are maximised while risk is minimised. MPT is also often called mean-variance analysis, as the ‘optimal’ portfolio weights (\(w^*\)) can be found using the mean excess returns above the risk-free rate (\(\mu\)), covariance matrix (\(\Sigma\)), and the investor’s risk aversion coefficient (\(\lambda\)):
\[ \begin{aligned} w^* & = \frac{1}{\lambda} \Sigma^{-1} \mu \\ \end{aligned} \]
Here, \(\mu = E[R] - r_f \mathbf{1}\) represents expected excess returns over the risk-free asset, and \(\lambda\) denotes the investor’s risk aversion coefficient—specifically, the units of excess return demanded per unit of portfolio variance risk:
\[ \lambda = \frac{E[R_m] - r_f}{\sigma_m^2} = \frac{SR_m}{\sigma_m} \]
where \(SR_m\) is the market’s Sharpe ratio and \(\sigma_m\) is market volatility.
Although Markowitz developed a theoretically solid framework, it is prone to large estimation errors when used with sample moments for the mean and variance. The BL model starts with an asset allocation based on the equilibrium assumption (derived from market capitalisation weights via reverse optimisation) and then modifies this allocation by considering the opinions of the investor regarding future asset performance, including their uncertainties.
The neat idea of the BL model is how it uses Bayesian updating to weight and map experts’ views to market data. Some of the concepts are very similar to the Kalman filter.
I will walk through the ideas in several steps:
- Bayesian updating
- Defining prior, observations, and the Kalman trick
- Example application of the BL model
Bayesian updating
The starting point is that we assume the returns of our assets follow a multivariate normal distribution, i.e., for \(k\) assets the returns can be described as:
\[ \begin{aligned} r|(\mu, \Sigma) & \sim \mathsf{Normal}(\mu, \Sigma)\\ \end{aligned} \]
That’s the likelihood distribution explaining how we believe the data is generated with known parameters.
Let’s assume the covariance matrix \(\Sigma\) is known, but that we are uncertain about the mean return \(\mu\).
Suppose experts have provided us with a prior guess on \(\mu\), which we name \(\mu_0\), and their degree of certainty, as expressed by the covariance \(\Sigma_0\). The ‘real’ \(\mu\) is believed to follow a multivariate normal distribution with parameters \(\mu_0\) and \(\Sigma_0\):
\[ \begin{aligned} \mu & \sim \mathsf{Normal}\left(\mu_0, \Sigma_0\right)\\ \end{aligned} \]
That’s the prior parameter distribution.
In addition, suppose we have collected return data \(r_D\) of sample size \(n\) that we believe came from our likelihood distribution.
Bayes’ rule tells us how we can use the likelihood and prior distribution to update our belief about the parameters \(\theta\), after we have observed data \(D\):
\[ \begin{aligned} P(\theta | D) & = \frac{P(D|\theta) P(\theta)}{P(D)}\\ \end{aligned} \]
In our case, using multivariate normal distributions with known covariances, the updated posterior hyper-parameters can be calculated as (see conjugate prior):
\[ \begin{aligned} \mu_1 & = \left(\Sigma_0^{-1} + n \Sigma^{-1}\right)^{-1} \left(\Sigma_0^{-1}\mu_0 + n \Sigma^{-1} \bar{r}\right) \\ \Sigma_1 & = \left(\Sigma_0^{-1} + n \Sigma^{-1}\right)^{-1} \end{aligned} \]
Here \(\bar{r}\) is the sample average of \(r_D\) over the data. The updated hyper-parameters \(\mu_1, \Sigma_1\) are credibility-weighted averages.
Using the matrix inversion lemma \((A^{-1} + B^{-1})^{-1} = A - A(A+B)^{-1}A = A(A+B)^{-1}B\) with \(A = \Sigma_0\) and \(B = \frac{1}{n}\Sigma\), the above expression can also be written as:
\[ \begin{aligned} \mu_1 & = \mu_0 + \Sigma_0 \left(\Sigma_0 + \frac{1}{n} \Sigma\right)^{-1} \left( \bar{r} - \mu_0\right) \\ \Sigma_1 & = \Sigma_0 - \Sigma_0 \left(\Sigma_0 + \frac{1}{n} \Sigma\right)^{-1}\Sigma_0 \end{aligned} \]
The posterior predictive distribution, i.e., generating ‘new’ data \(\tilde{r}\) given the observations thus far, combines the updated posterior mean \(\mu_1\) and parameter uncertainty \(\Sigma_1\), plus the known data-generating variance \(\Sigma\) from the likelihood:
\[ \begin{aligned} \tilde{r}|(r_D,\mu_0, \Sigma_0, \Sigma) & \sim \mathsf{Normal}(\mu_1, \Sigma_1 + \Sigma) \end{aligned} \]
Defining prior, observations, and the Kalman trick
To use the Bayesian updating calculations from the previous section for our portfolio, we have to define what information we can use as our prior and observations.
According to BL, the information we get from market data can be seen as prior information, while the expert’s views on portfolio performance are treated as future observations.
Arguably, market prices already reflect consensus expectations of future performance. That is what sets the market price of an asset today. We can express this prior distribution as:
\[ \begin{aligned} \mu & \sim \mathsf{Normal}\left(\mu_0, \tau \Sigma\right) \end{aligned} \]
Here \(\mu_0\) represents the equilibrium market excess returns and \(\tau\) is a scaling factor for \(\Sigma\) (rather than estimating an independent covariance matrix \(\Sigma_0\)). Because \(0 < \tau \ll 1\), we are assuming that the expected mean return varies considerably less than the actual asset returns themselves.
There is a clear similarity to the Kalman filter here: we have a ‘process model’ predicting the state (market equilibrium) and ‘sensors’ collecting observations to correct the prediction (expert views). We distinguish between public views (market equilibrium) and private views (analyst forecasts).
Furthermore, in many Kalman filter applications, sensor observations are not in the same coordinate space as the state vector, requiring a linear projection matrix. The BL model employs this exact mechanism, allowing experts to specify not just absolute returns, but also relative performance. Predicting relative outperformance is often easier than predicting absolute levels (analogous to predicting the winner of a match rather than the exact scoreline).
This introduces the so-called pick matrix \(P\). The matrix \(P\) has \(k\) columns (reflecting the \(k\) assets in our portfolio) and \(m\) rows (one for each view). For absolute views, the row entries sum to \(1\); for relative views, the row entries sum to \(0\). Multiplying the pick matrix by \(\mu\) represents the expert’s views on expected returns. The uncertainties of these views are captured in the covariance matrix \(\Omega\):
\[ \begin{aligned} q & \sim \mathsf{Normal}\left(P\mu, \Omega \right) \end{aligned} \]
Applying the Bayesian updating formula with the projection matrix \(P\) yields:
\[ \begin{aligned} \mu_{BL} & = \mu_0 + \tau\Sigma P^T \left(P\tau\Sigma P^T + \Omega\right)^{-1} \left( q - P \mu_0\right) \\ \Sigma_{BL} & = \tau \Sigma - \tau \Sigma P^T \left(P \tau \Sigma P^T + \Omega \right)^{-1} P \tau \Sigma \end{aligned} \]
Defining the Kalman gain matrix \(K = \Sigma P^T \left(P \tau \Sigma P^T + \Omega \right)^{-1}\), this simplifies to:
\[ \begin{aligned} \mu_{BL} & = \mu_0 + \tau K \left( q - P \mu_0\right) \\ \Sigma_{BL} & = \tau \left(\Sigma - \tau K P \Sigma \right) \end{aligned} \]
Alternatively, in precision (information) matrix form:
\[ \begin{aligned} \mu_{BL} & = \left((\tau\Sigma)^{-1} + P^T\Omega^{-1}P\right)^{-1} \left((\tau\Sigma)^{-1} \mu_0 + P^T\Omega^{-1}q\right) \\ \Sigma_{BL} & = \left((\tau\Sigma)^{-1} + P^T\Omega^{-1}P\right)^{-1} \end{aligned} \]
Either way, this provides the hyper-parameters for our posterior predictive distribution of asset returns:
\[ \begin{aligned} r|\mu_0, q,\tau,\Sigma,\Omega & \sim \mathsf{Normal}\left(\mu_{BL}, \Sigma_{BL} + \Sigma\right) \end{aligned} \]
Before moving to the empirical example, let us revisit the covariance matrices \(\Sigma\) and \(\Omega\). Because expert views represent linear projections of returns (\(q = Pr + \epsilon\)), their covariance relates directly to \(\Sigma\):
\[ \begin{aligned} \mathsf{Normal}(P\mu, \Omega) & = P \,\mathsf{Normal}(\mu, \Sigma) \\ &\Rightarrow \Omega = P \Sigma P^T \end{aligned} \]
As (Maggiar (2009)) notes, practitioners sometimes assume views are uncorrelated by setting \(\Omega = \mathsf{diag}(P \Sigma P^T)\), or scale \(\Omega = \tau P \Sigma P^T\) to prevent views from dominating or being dominated by the prior. In this example, we maintain \(\Omega = P \Sigma P^T\). Note that because \(\operatorname{Var}(\mu) = \tau \Sigma\), setting \(\Omega = P \Sigma P^T\) gives the prior roughly \(\frac{1}{\tau}\) times the weight of the views.
Example application of the BL model
For this example, we select seven well-known US equities spanning distinct sectors:
- Aviation / defence: Boeing
- Insurance: Chubb
- Entertainment: Disney
- Travel: Hilton
- Technology: IBM
- Food & drinks: Coca-Cola
- Energy: Exxon
Setup
We begin by loading the required R packages, ticker symbols, and market capitalisations.
library(yfR) # get stock data
library(data.table) # data manipulation
library(zoo) # working with time series
library(ggplot2) # data visualisation
library(scales) # scale functions for visualisation
library(GGally) # plotting multivariate data
library(gt) # displaying tables
library(MASS) # multivariate normal distribution
library(corrplot) # visualise correlation matrix
comp_name <- c("Boeing", "Chubb", "Disney", "Hilton", "IBM",
"Coca-Cola", "Exxon")
ticker <- c("BA", "CB", "DIS", "HLT", "IBM", "KO", "XOM")
mkt_cap_USD_bn <- c(BA = 125, CB = 87, DIS = 202,
HLT = 40, IBM = 124, KO = 259, XOM = 457)
mkt_cap_weight <- mkt_cap_USD_bn / sum(mkt_cap_USD_bn)
comp_dt <- data.table(comp_name, ticker, mkt_cap_USD_bn, mkt_cap_weight)Historical return data
We collect ten years of monthly adjusted close prices, putting all assets on an equal footing regarding dividend payouts and stock splits, while allowing rolling five-year window analysis.
end_study_date <- as.Date("2023-02-14")
stock_dt <- data.table(na.omit(
yf_get(tickers = ticker,
first_date = end_study_date - 10 * 365,
last_date = end_study_date,
freq_data = "monthly")))
# Combine stock data with company information
setkey(stock_dt, ticker)
setkey(comp_dt, ticker)
price_dt <- comp_dt[stock_dt]
# Create wide table of monthly adjusted returns
adj_returns <- dcast.data.table(
stock_dt,
ref_date ~ ticker,
value.var = "ret_adjusted_prices"
)Let’s inspect the historical prices by company:
ggplot(price_dt,
aes(x = ref_date,
y = price_adjusted)) +
geom_line() +
labs(x = "Month", y = "USD",
title = "Monthly adjusted close prices",
subtitle = paste(min(price_dt$ref_date), "to",
max(price_dt$ref_date))) +
facet_wrap(~comp_name, scales = 'free_y') 
The historical series illustrates how different sectors absorbed the impact of COVID-19 in early 2020 and the onset of the war in Ukraine in early 2022. Pairwise return plots confirm that the multivariate normal distribution serves as a workable likelihood approximation:
ggpairs(adj_returns[, -1],
lower = list(continuous = wrap("points", alpha = 0.3, size = 1),
combo = wrap("dot", alpha = 0.4, size = 0.2)))
Next, we calculate the rolling five-year window of monthly average returns and volatility:
annualised_mean <- function(x){
(1 + mean(x))^12 - 1
}
rolling_5yr_dt <- na.omit(price_dt[, .(
end_date = zoo::as.Date(frollapply(ref_date, 5 * 12, max)),
rolling_5_yr_mean = frollapply(ret_adjusted_prices, 5 * 12, annualised_mean),
rolling_5_yr_sd = frollapply(ret_adjusted_prices, 5 * 12, sd) * sqrt(12)
), by = comp_name])
ggplot(rolling_5yr_dt,
aes(y = rolling_5_yr_mean,
x = rolling_5_yr_sd,
col = end_date)) +
labs(
title = "Rolling 5-year adjusted close price returns",
x = "Rolling 5-year SD (%)",
y = "Rolling 5-year mean (%)") +
geom_point() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent) +
guides(col = guide_legend(title = "End date")) +
facet_wrap(~comp_name) 
To estimate the covariance matrix \(\Sigma\) and implied excess returns \(\mu_0\), we use the most recent five-year window of our dataset and assume an annual risk-free rate of \(3.5\%\).
# Anchor 5-year window to data end date rather than Sys.Date()
start_date <- max(adj_returns$ref_date) - 5 * 365.25
rf <- 3.5 / 100 # Risk-free rate assumption
# Estimate annualised covariance from monthly return data
Sigma <- cov(adj_returns[ref_date >= start_date, -1]) * 12
round(Sigma, 3)## BA CB DIS HLT IBM KO XOM
## BA 0.214 0.045 0.076 0.071 0.054 0.035 0.076
## CB 0.045 0.055 0.039 0.043 0.025 0.025 0.040
## DIS 0.076 0.039 0.116 0.076 0.026 0.028 0.056
## HLT 0.071 0.043 0.076 0.102 0.035 0.031 0.063
## IBM 0.054 0.025 0.026 0.035 0.069 0.023 0.045
## KO 0.035 0.025 0.028 0.031 0.023 0.035 0.024
## XOM 0.076 0.040 0.056 0.063 0.045 0.024 0.121corrplot(cov2cor(Sigma), method = "pie", title = "Correlation",
mar = c(.25, .1, 2.5, .1), tl.srt = 45, diag = FALSE, type = "upper")
We set the uncertainty scaling parameter \(\tau\) to \(0.20\) (\(20\%\)).
tau <- 0.20
# Estimate historical benchmark portfolio return
setkey(stock_dt, ticker)
setkey(comp_dt, ticker)
total_return <- price_dt[ref_date >= start_date, .(
adj_ret = sum(ret_adjusted_prices * mkt_cap_weight)),
by = ref_date]
# Estimate excess equilibrium returns by reverse optimisation
portfolio_mean_r <- annualised_mean(total_return$adj_ret)
portfolio_var <- var(total_return$adj_ret) * 12
lambda <- (portfolio_mean_r - rf) / portfolio_var
# Implied equilibrium excess returns: mu0 = lambda * Sigma * w_mkt
mu0 <- c(lambda) * (Sigma %*% mkt_cap_weight[colnames(Sigma)])
mu0_dt <- data.table(
ticker,
equilibrium_er = as.numeric(mu0),
key = "ticker")
comp_mean_r_dt <- stock_dt[ref_date >= start_date, .(
hist_5yr_mean_r = ((1 + mean(ret_adjusted_prices))^12 - 1)),
keyby = ticker]
summary_dt <- comp_dt[comp_mean_r_dt][mu0_dt]| comp_name | ticker | mkt_cap_weight | hist_5yr_mean_r | equilibrium_er |
|---|---|---|---|---|
| Boeing | BA | 9.7% | 2.2% | 11.0% |
| Chubb | CB | 6.7% | 11.7% | 5.3% |
| Disney | DIS | 15.6% | 6.1% | 8.3% |
| Hilton | HLT | 3.1% | 17.8% | 8.1% |
| IBM | IBM | 9.6% | 6.0% | 5.6% |
| Coca-Cola | KO | 20.0% | 10.1% | 4.0% |
| Exxon | XOM | 35.3% | 18.5% | 10.4% |
Expert portfolio views
Suppose our expert expresses three distinct views: 1. Boeing will outperform Hilton by \(5\%\). 2. Disney will outperform an equally weighted basket of Coca-Cola and Exxon by \(1\%\). 3. Chubb will outperform an equally weighted basket of IBM and Coca-Cola by \(3\%\).
We translate these views into the pick matrix \(P\), view vector \(q\), and view uncertainty matrix \(\Omega\):
nViews <- 3
P <- matrix(
c(1, 0, 0, -1, 0, 0, 0, # BA - HLT = 0.05
0, 0, 1, 0, 0, -0.5, -0.5, # DIS - 0.5(KO + XOM) = 0.01
0, 1, 0, 0, -0.5, -0.5, 0), # CB - 0.5(IBM + KO) = 0.03
nrow = nViews, byrow = TRUE
)
q <- c(0.05, 0.01, 0.03)
Omega <- P %*% Sigma %*% t(P)Black-Litterman model calculations
With the prior parameters (\(\mu_0, \tau \Sigma\)) and view parameters (\(P, q, \Omega\)) established, we compute the Black-Litterman posterior returns and portfolio allocations:
# Kalman gain matrix
K <- Sigma %*% t(P) %*% solve(tau * P %*% Sigma %*% t(P) + Omega)
# Posterior mean excess returns
BL_mean_er <- mu0 + tau * K %*% (q - P %*% mu0)
# Posterior parameter covariance
BL_Sigma <- tau * (Sigma - tau * K %*% P %*% Sigma)
# CHANGE: Added comment explaining why solve(Sigma) is used over solve(Sigma + BL_Sigma)
# Following He & Litterman (1999), inverting Sigma ensures that neutral views
# reproduce benchmark weights exactly (w* = w_mkt when q = P %*% mu0).
BL_weight <- solve(Sigma) %*% BL_mean_er / lambda
# Posterior predictive return covariance
BL_Sigma_Portfolio <- Sigma + BL_Sigma
summary_dt[, BL_mean_er := BL_mean_er]
summary_dt[, BL_weight := BL_weight]| comp_name | mkt_cap_weight | BL_weight | equilibrium_er | BL_mean_er |
|---|---|---|---|---|
| Boeing | 9.7% | 11.4% | 11.0% | 11.4% |
| Chubb | 6.7% | 14.3% | 5.3% | 5.6% |
| Disney | 15.6% | 14.6% | 8.3% | 8.4% |
| Hilton | 3.1% | 1.3% | 8.1% | 8.1% |
| IBM | 9.6% | 5.8% | 5.6% | 5.4% |
| Coca-Cola | 20.0% | 16.8% | 4.0% | 4.0% |
| Exxon | 35.3% | 35.8% | 10.4% | 10.5% |
Notice that while the expert’s bullish stance on Boeing and Chubb increased their allocations, Disney’s weight declined slightly. This reveals an important insight of the Black-Litterman model: views are evaluated relative to the market equilibrium baseline, not in isolation.
In our market equilibrium vector \(\mu_0\), the market was already pricing Disney to outperform the defensive basket of Coca-Cola and Exxon by more than \(1\%\). Asserting an outperformance of only \(1\%\) represents an effective downgrade relative to prior equilibrium expectations.
with(summary_dt,
barplot(t(cbind(equilibrium_er, BL_mean_er)) * 100,
ylim = c(0, 15),
beside = TRUE, names.arg = comp_name,
legend = c("Equilibrium excess return", "BL excess return"),
border = NA, ylab = "Excess return (%)",
args.legend = list(bty = "n")))
title("Excess returns", adj = 0)
with(summary_dt,
barplot(t(cbind(mkt_cap_weight, BL_weight) * 100),
ylim = c(0, 45),
beside = TRUE, names.arg = comp_name,
legend = c("Mkt cap weight", "BL weight"),
border = NA, ylab = "Portfolio weight (%)",
args.legend = list(bty = "n")))
title("Portfolio weights", adj = 0)
Although the posterior returns \(\mu_{BL}\) stay relatively close to \(\mu_0\) (because \(\Omega = P\Sigma P^T\) gives roughly \(83\%\) weight to the market prior and \(17\%\) to the views when \(\tau = 0.20\)), the portfolio weights tilt substantially. Because unconstrained mean-variance optimization is exceptionally sensitive to expected return differentials across correlated assets, even slight adjustments in relative returns produce significant active tilts.
Posterior predictive returns
We can now simulate future returns using the posterior predictive distribution:
nSims <- 1e5
sample_rt <- mvrnorm(nSims, BL_mean_er, BL_Sigma_Portfolio)
sample_rt_portfolio <- as.numeric(sample_rt %*% BL_weight)Let’s compare the historical five-year rolling portfolio performance with the simulated five-year trajectory distribution:
past_returns <- price_dt[, .(
adj_ret = sum(ret_adjusted_prices * mkt_cap_weight)),
by = ref_date]
# Historical 60-month rolling statistics
past_rolling_5_yr <- na.omit(past_returns[, .(
end_date = zoo::as.Date(frollapply(ref_date, 60, max)),
rolling_5_yr_mean = frollapply(adj_ret, 60, annualised_mean),
rolling_5_yr_sd = frollapply(adj_ret, 60, sd) * sqrt(12)
)])
# Group annual simulated draws into non-overlapping 5-year investment horizons
n_blocks <- floor(nSims / 5)
sim_blocks <- matrix(sample_rt_portfolio[1:(n_blocks * 5)], ncol = 5, byrow = TRUE)
bl_sims_rolling_5_yr <- data.table(
rolling_5_yr_mean = apply(sim_blocks, 1, mean),
rolling_5_yr_sd = apply(sim_blocks, 1, sd)
)
ggplot(past_rolling_5_yr, aes(x = rolling_5_yr_sd, y = rolling_5_yr_mean)) +
geom_point(aes(colour = "Historical 5-year rolling")) +
geom_density_2d(data = bl_sims_rolling_5_yr,
aes(x = rolling_5_yr_sd, y = rolling_5_yr_mean, colour = "BL simulated density")) +
scale_colour_manual(name = "Data source",
values = c("Historical 5-year rolling" = "black",
"BL simulated density" = "steelblue")) +
labs(
title = "Rolling 5-year returns vs. simulated future performance",
subtitle = paste("Historical sample:", min(past_returns$ref_date), "to", max(past_returns$ref_date)),
x = "Annualised standard deviation (%)",
y = "Annualised mean return (%)") +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent) +
theme_minimal() +
theme(legend.position = "bottom")
The historical observations reflect the specific path of recent market shocks (the pandemic crash, recovery, and subsequent geopolitical conflicts). The simulated density, by contrast, centres around the forward-looking equilibrium modified by expert views.
The primary contribution of the Black-Litterman framework is precisely this Bayesian synthesis: rather than extrapolating noisy historical sample means into unstable portfolio allocations, it anchors expectations to the clearing market equilibrium while offering a mathematically disciplined mechanism to express private views.
Session Info
session_info <- (sessionInfo()[-8])
utils:::print.sessionInfo(session_info, local = FALSE)## R version 4.6.2 (2026-10-29)
## Platform: aarch64-apple-darwin23
## Running under: macOS Golden Gate 27.2
##
## Matrix products: default
## BLAS: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRblas.0.dylib
## LAPACK: /Library/Frameworks/R.framework/Versions/4.6/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
##
## attached base packages:
## NULL
##
## other attached packages:
## [1] corrplot_0.95 MASS_7.3-66 gt_1.4.0
## [4] GGally_2.4.0 scales_1.4.0 ggplot2_4.0.3
## [7] zoo_1.9-1 data.table_1.18.6.1 yfR_1.1.3
##
## loaded via a namespace (and not attached):
## [1] gtable_0.3.6 xfun_0.62 bslib_0.12.0 pingr_2.0.5
## [5] processx_3.9.0 lattice_0.23-1 tzdb_0.5.0 vctrs_0.7.3
## [9] tools_4.6.2 generics_0.1.4 curl_8.1.0 parallel_4.6.2
## [13] tibble_3.3.1 xts_0.14.3 pkgconfig_2.0.3 RColorBrewer_1.1-3
## [17] S7_0.2.2 lifecycle_1.0.5 compiler_4.6.2 farver_2.1.2
## [21] stringr_1.6.0 codetools_0.2-20 htmltools_0.5.9 sass_0.4.10
## [25] yaml_2.3.12 pillar_1.11.1 crayon_1.5.3 jquerylib_0.1.4
## [29] tidyr_1.3.2 cachem_1.1.0 ggstats_0.14.0 tidyselect_1.2.1
## [33] digest_0.6.39 stringi_1.8.9 dplyr_1.2.1 purrr_1.2.2
## [37] bookdown_0.48 labeling_0.4.3 fastmap_1.2.0 grid_4.6.2
## [41] cli_3.6.6 magrittr_2.0.5 readr_2.2.0 withr_3.0.3
## [45] bit64_4.8.6 lubridate_1.9.5 timechange_0.4.0 TTR_0.24.4
## [49] rmarkdown_2.32 quantmod_0.4.29 bit_4.6.0 otel_0.2.0
## [53] blogdown_1.24 hms_1.1.4 evaluate_1.0.5 knitr_1.52
## [57] rlang_1.3.0 isoband_0.3.0 glue_1.8.1 xml2_1.6.0
## [61] rstudioapi_0.19.0 vroom_1.7.1 jsonlite_2.0.0 R6_2.6.1
## [65] fs_2.1.0References
Citation
For attribution, please cite this work as:Markus Gesmann (Feb 15, 2023) Portfolio Allocation for Bayesian Dummies. Retrieved from https://magesblog.com/post/2023-02-15-portfolio-allocation-for-bayesian-dummies/
@misc{ 2023-portfolio-allocation-for-bayesian-dummies,
author = { Markus Gesmann },
title = { Portfolio Allocation for Bayesian Dummies },
url = { https://magesblog.com/post/2023-02-15-portfolio-allocation-for-bayesian-dummies/ },
year = { 2023 }
updated = { Feb 15, 2023 }
}