knitr::opts_chunk$set( collapse = TRUE, comment = "#>" )
This brief introduction shows how use CMA to price alternative scenarios for an investment strategy. The recipe goes as follows:
Detach the copulas from the margins;
Pick one element to play with (copula, marginals, correlations, etc);
Generate new scenarios;
Merge the newly generated scenarios with the empirical copulas (or margins).
For this vignette, the EuStockMarket
dataset is used as proxy for the "Market".
The setup goes as follows:
# load packages library(cma) # core package library(dplyr) # data manipulation library(tidyr) # data manipulation library(purrr) # vetorized loops library(ggplot2) # plotting # compute log returns (stationarity) x <- matrix(diff(log(EuStockMarkets)), ncol = 4) n_row <- nrow(x) head(x)
The first step evolves separating the margins from the copulas with cma_separation()
.
# First CMA Step step_one <- cma_separation(x) step_one
The output is a list with three components: the margins, the CDF, and the copula.
In this vignette, the margins are manipulated while keeping the copula structure unchanged.
Once the the "separation" is done it's necessary to choose a target distribution for the data. For simplicity, let's use a Student-t, but it could be any distribution (see "Marginal Distributions" on the reference page).
dist_t <- fit_t(step_one$marginal) dist_t
The next step requires some tidyverse
skills.
Start building a list-column for the new scenarios wit generate_margins()
:
simul_tbl <- tibble::tibble(simulations = 1:100) |> dplyr::mutate(new_scenarios = purrr::map( .x = rep(n_row, 100), .f = ~ generate_margins(model = dist_t, n = .x) ) ) |> tidyr::unnest(cols = new_scenarios) simul_tbl
The simul_tbl
is a tidy (long-format) tibble
with 100 marginal distributions that are consistent with the dist_t
object fitted in step 2 (every row in new_scenarios
holds a different simulation).
To complete the scenario generation process it's important to "glue" the simulations back into the empirical copula that was stored in the object step_one
.
step_two <- simul_tbl |> dplyr::mutate(cma_comb = purrr::map( .x = new_scenarios, .f = ~ cma_combination( x = .x, cdf = step_one$cdf, copula = step_one$copula) ) ) step_two
The cma_comb
column now holds 100 simulations that, although being different, share a bond to each other because the structure of their copula is the same.
Finally, it's possible to compute the performance of those realizations over an equal-weight strategy, for example (it could be any strategy).
step_two <- step_two |> dplyr::mutate(weights = list(rep(0.25, 4)), pnl = purrr::map2(.x = cma_comb, .y = weights, .f = ~ as.matrix(.x) %*% .y)) |> dplyr::select(-c(cma_comb, weights)) step_two
Translate compounded returns to prices and plot:
step_two |> # price dplyr::mutate(pnl_prices = purrr::map(.x = pnl, .f = ~ cumprod(exp(.x))), pnl_ref = list(cumprod(exp(x %*% rep(0.25, 4))))) |> tidyr::unnest(cols = c(pnl_prices, pnl_ref)) |> # build an index dplyr::group_by(simulations) |> dplyr::mutate(rowid = 1:n_row) |> dplyr::ungroup() |> # plot ggplot2::ggplot(ggplot2::aes(x = rowid, y = pnl_prices, group = simulations)) + ggplot2::geom_line(col = "grey") + ggplot2::geom_line(ggplot2::aes(y = pnl_ref), color = "purple") + ggplot2::scale_y_log10(labels = scales::dollar_format()) + ggplot2::labs(title = "EuStockMarkets Equal Weight Strategy", subtitle = "Realized vs. Alternative Scenarios", x = "Time Evolution", y = "P&L")
All the paths generated above are compatible with the same marginal distribution specification. The difference between the "good" portfolios and the "bad" ones is purely due by chance.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.