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:

  1. Detach the copulas from the margins;

  2. Pick one element to play with (copula, marginals, correlations, etc);

  3. Generate new scenarios;

  4. 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)

Step 1. Separate the Copulas from the Margins

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.

Step 2. Choose a Target Distribution

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

Step 3. Create New Scenarios

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).

Step 4. "Glue" the New Scenarios Into the Empirical Copula

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.



Reckziegel/CMA documentation built on July 13, 2022, 10:31 p.m.