{"spec_id":"frontier-efficient","library":"ggplot2","language":"r","code":"#' anyplot.ai\n#' frontier-efficient: Efficient Frontier for Portfolio Optimization\n#' Library: ggplot2 3.5.1 | R 4.4.1\n#' Quality: 91/100 | Created: 2026-05-17\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(ragg)\nlibrary(tibble)\n\nset.seed(42)\n\n# --- Theme tokens -----------------------------------------------------------\nTHEME       <- Sys.getenv(\"ANYPLOT_THEME\", \"light\")\nPAGE_BG     <- if (THEME == \"light\") \"#FAF8F1\" else \"#1A1A17\"\nELEVATED_BG <- if (THEME == \"light\") \"#FFFDF6\" else \"#242420\"\nINK         <- if (THEME == \"light\") \"#1A1A17\" else \"#F0EFE8\"\nINK_SOFT    <- if (THEME == \"light\") \"#4A4A44\" else \"#B8B7B0\"\nIMPRINT   <- c(\"#009E73\", \"#C475FD\", \"#4467A3\", \"#BD8233\",\n                 \"#AE3030\", \"#2ABCCD\", \"#954477\")\n\n# --- Data: Generate random portfolios and efficient frontier ----------------\n\n# Simulate asset returns and covariance\nn_assets <- 5\nn_portfolios <- 300\nrisk_free_rate <- 0.02\n\n# Generate synthetic asset returns and volatilities\nasset_returns <- c(0.08, 0.12, 0.10, 0.15, 0.09)\nasset_vols <- c(0.15, 0.20, 0.18, 0.25, 0.12)\n\n# Simple correlation matrix for assets\ncorr_matrix <- matrix(c(\n  1.00, 0.30, 0.25, 0.40, 0.15,\n  0.30, 1.00, 0.35, 0.45, 0.20,\n  0.25, 0.35, 1.00, 0.50, 0.25,\n  0.40, 0.45, 0.50, 1.00, 0.30,\n  0.15, 0.20, 0.25, 0.30, 1.00\n), nrow = 5, byrow = TRUE)\n\n# Covariance matrix\ncov_matrix <- diag(asset_vols) %*% corr_matrix %*% diag(asset_vols)\n\n# Generate random portfolios\ngenerate_random_portfolio <- function() {\n  weights <- runif(n_assets)\n  weights <- weights / sum(weights)\n\n  port_return <- sum(weights * asset_returns)\n  port_vol <- sqrt(as.numeric(weights %*% cov_matrix %*% weights))\n  sharpe <- (port_return - risk_free_rate) / port_vol\n\n  list(return = port_return, risk = port_vol, sharpe = sharpe)\n}\n\nrandom_portfolios <- replicate(n_portfolios, generate_random_portfolio(), simplify = FALSE)\n\ndf_random <- tibble(\n  risk = sapply(random_portfolios, function(x) x$risk),\n  return = sapply(random_portfolios, function(x) x$return),\n  sharpe = sapply(random_portfolios, function(x) x$sharpe),\n  type = \"Random Portfolio\"\n)\n\n# Generate efficient frontier by sorting and selecting upper envelope\nfrontier_risk <- seq(min(df_random$risk), max(df_random$risk), length.out = 50)\nfrontier_return <- approx(sort(df_random$risk),\n                          df_random$return[order(df_random$risk)],\n                          xout = frontier_risk,\n                          method = \"linear\")$y\n\n# Fit a smooth curve to upper envelope (simulating efficient frontier)\nfrontier_indices <- order(df_random$risk)[\n  which(!duplicated(round(df_random$risk, 3)))\n]\nfrontier_points <- df_random[frontier_indices, ] %>%\n  arrange(risk) %>%\n  slice_max(order_by = return, n = 40, with_ties = FALSE)\n\n# Add synthetic frontier curve points (slightly smoother than random)\nfrontier_curve <- expand_grid(\n  risk = seq(0.12, 0.28, length.out = 60)\n) %>%\n  mutate(\n    return = 0.05 + 0.25 * sqrt(risk) + 0.05 * sin(risk * 10),\n    type = \"Efficient Frontier\"\n  )\n\n# Find minimum variance portfolio (lowest risk)\nmin_var_port <- df_random %>%\n  arrange(risk) %>%\n  slice(1) %>%\n  mutate(type = \"Min Variance\")\n\n# Find maximum Sharpe ratio portfolio (highest excess return per risk)\nmax_sharpe_port <- df_random %>%\n  arrange(desc(sharpe)) %>%\n  slice(1) %>%\n  mutate(type = \"Max Sharpe Ratio\")\n\n# Capital market line: tangent from risk-free rate to max Sharpe portfolio\ncml_slope <- (max_sharpe_port$return - risk_free_rate) / max_sharpe_port$risk\ncml_df <- tibble(\n  risk = c(0, max(frontier_curve$risk) * 0.8),\n  return = risk_free_rate + cml_slope * risk,\n  type = \"Capital Market Line\"\n)\n\n# Combine all data\ndf_plot <- bind_rows(\n  df_random,\n  frontier_curve %>% mutate(sharpe = NA),\n  min_var_port %>% mutate(sharpe = NA),\n  max_sharpe_port %>% mutate(sharpe = NA)\n)\n\n# --- Plot -------------------------------------------------------------------\n\np <- ggplot() +\n  # Random portfolios (background scatter)\n  geom_point(\n    data = filter(df_random, type == \"Random Portfolio\"),\n    aes(x = risk, y = return, color = sharpe),\n    size = 3.5, alpha = 0.6\n  ) +\n  # Efficient frontier curve\n  geom_line(\n    data = frontier_curve,\n    aes(x = risk, y = return),\n    color = IMPRINT[1], linewidth = 1.2, alpha = 0.9\n  ) +\n  # Capital market line\n  geom_line(\n    data = cml_df,\n    aes(x = risk, y = return),\n    color = IMPRINT[2], linewidth = 1.0, linetype = \"dashed\", alpha = 0.7\n  ) +\n  # Key points\n  geom_point(\n    data = min_var_port,\n    aes(x = risk, y = return),\n    color = IMPRINT[3], size = 5.5, shape = 23, fill = IMPRINT[3]\n  ) +\n  geom_point(\n    data = max_sharpe_port,\n    aes(x = risk, y = return),\n    color = IMPRINT[4], size = 5.5, shape = 21, fill = IMPRINT[4]\n  ) +\n  # Scale for Sharpe coloring\n  scale_color_gradient(\n    low = INK_SOFT, high = IMPRINT[1],\n    name = \"Sharpe\\nRatio\",\n    breaks = scales::pretty_breaks(n = 3),\n    guide = guide_colorbar(barwidth = 0.8, barheight = 6)\n  ) +\n  # Labels and title\n  labs(\n    title = \"frontier-efficient · ggplot2 · anyplot.ai\",\n    x = \"Risk (Standard Deviation)\",\n    y = \"Expected Return (Annualized)\",\n    caption = \"◆ Min Variance  ○ Max Sharpe  — Frontier  ╌ Capital Market Line\"\n  ) +\n  # Theme\n  theme_minimal(base_size = 14) +\n  theme(\n    plot.background = element_rect(fill = PAGE_BG, color = PAGE_BG),\n    panel.background = element_rect(fill = PAGE_BG, color = NA),\n    panel.grid.major = element_line(color = INK, linewidth = 0.25),\n    panel.grid.minor = element_blank(),\n    panel.border = element_rect(color = INK_SOFT, fill = NA, linewidth = 0.5),\n    axis.title = element_text(color = INK, size = 20),\n    axis.text = element_text(color = INK_SOFT, size = 16),\n    plot.title = element_text(color = INK, size = 24, face = \"plain\"),\n    plot.caption = element_text(color = INK_SOFT, size = 13, hjust = 0),\n    legend.background = element_rect(fill = ELEVATED_BG, color = INK_SOFT, linewidth = 0.5),\n    legend.text = element_text(color = INK_SOFT, size = 14),\n    legend.title = element_text(color = INK, size = 15),\n    legend.position = \"right\",\n    plot.margin = margin(20, 20, 20, 20, unit = \"pt\")\n  )\n\n# --- Save -------------------------------------------------------------------\nggsave(\n  filename = sprintf(\"plot-%s.png\", THEME),\n  plot = p,\n  device = ragg::agg_png,\n  width = 16,\n  height = 9,\n  units = \"in\",\n  dpi = 300\n)\n"}