Instructions Before You Knit
- Replace the placeholder Yahoo Finance tickers below with your own
three assets.
- Check that all three assets have aligned daily observations covering
at least five years and ending on 17 February 2026.
- Review every method against your lecture notes and Moodle guidance
before submission.
- Write your own interpretation in the
TODO
sections.
- For submission, knit to PDF. The HTML output is still useful while
drafting.
asset_a <- "SPY"
asset_b <- "GLD"
asset_c <- "TLT"
symbols <- c(asset_a, asset_b, asset_c)
analysis_start <- "2020-01-01"
analysis_end <- "2026-02-17"
Data Download And Alignment
prices <- get_adjusted_prices(
symbols = symbols,
from = analysis_start,
to = analysis_end
)
returns <- simple_returns(prices)
price_summary <- data.frame(
asset = colnames(prices),
first_date = as.character(start(prices)),
last_date = as.character(end(prices)),
observations = nrow(prices)
)
kable(price_summary, caption = "Aligned adjusted closing prices")
Aligned adjusted closing prices
| SPY |
2020-01-02 |
2026-02-13 |
1538 |
| GLD |
2020-01-02 |
2026-02-13 |
1538 |
| TLT |
2020-01-02 |
2026-02-13 |
1538 |
saveRDS(prices, "aligned_prices.rds")
saveRDS(returns, "simple_returns.rds")
1. Asset A Analysis
1(a) Simple Daily Returns
asset_a_returns <- returns[, asset_a]
kable(head(data.frame(date = index(asset_a_returns), ret = coredata(asset_a_returns)), 10), digits = 6)
| 2020-01-03 |
-0.007572 |
| 2020-01-06 |
0.003815 |
| 2020-01-07 |
-0.002812 |
| 2020-01-08 |
0.005330 |
| 2020-01-09 |
0.006781 |
| 2020-01-10 |
-0.002878 |
| 2020-01-13 |
0.006877 |
| 2020-01-14 |
-0.001525 |
| 2020-01-15 |
0.002260 |
| 2020-01-16 |
0.008319 |
1(b) Statistical Features Of Asset A Returns
asset_a_stats <- sample_features(asset_a_returns)
kable(round(asset_a_stats, 6), caption = "Sample features for Asset A simple returns")
Sample features for Asset A simple returns
| 0.000624 |
0.012985 |
-0.263583 |
13.10959 |
-0.109424 |
0.105019 |
autoplot_data <- data.frame(
date = index(asset_a_returns),
ret = as.numeric(asset_a_returns)
)
ggplot(autoplot_data, aes(x = date, y = ret)) +
geom_line(color = "#1d3557", linewidth = 0.5) +
labs(x = "Date", y = "Simple return", title = paste("Daily returns for", asset_a))

TODO: Explain what the mean, standard deviation, skewness, and
kurtosis imply for risk management.
1(c)(i) Beginning Value Of A GBP 1 Million Portfolio
asset_a_value_path <- portfolio_value_path(asset_a_returns, end_value_millions = 1)
beginning_value_millions <- asset_a_value_path[1]
beginning_value_currency <- beginning_value_millions * 1000000
data.frame(
beginning_value_millions = round(beginning_value_millions, 6),
beginning_value_currency = round(beginning_value_currency, 2)
) %>%
kable(caption = "Implied beginning value of the Asset A portfolio")
Implied beginning value of the Asset A portfolio
| 0.433362 |
433362.3 |
1(c)(ii) VaR And ES For 18 February 2026
asset_a_risk_table <- method_comparison_table(
returns = asset_a_returns,
portfolio_value_millions = 1,
p = 0.95
)
kable(asset_a_risk_table %>% mutate(across(c(VaR, ES), round, 6)),
caption = "One-day 95% VaR and ES for Asset A, expressed in millions"
)
One-day 95% VaR and ES for Asset A, expressed in
millions
| Historical Simulation |
0.018023 |
0.031139 |
| Age-weighted Historical Simulation |
0.015449 |
0.022904 |
| Hull-White |
0.014988 |
0.021261 |
| Parametric Normal |
0.020735 |
0.026161 |
| Parametric Normal with Volatility Adjustment |
0.013739 |
0.017341 |
TODO: Check whether your course expects a different parametric
distribution than Normal. If yes, replace the parametric helper
accordingly.
1(c)(iii) Variation Across Approaches
TODO: Compare the five methods for the same risk measure and explain
why they differ.
1(c)(iv) Variation Between VaR And ES
TODO: Compare VaR and ES within each method and explain the practical
meaning.
2. Two-Asset VaR Using Hull-White
asset_b_returns <- returns[, asset_b]
# Equal-weight portfolio because £1m is invested in A and £1m in B
portfolio_ab_returns <- 0.5 * asset_a_returns + 0.5 * asset_b_returns
risk_a_hw <- hull_white_var_es(asset_a_returns, portfolio_value_millions = 1, p = 0.95)
risk_b_hw <- hull_white_var_es(asset_b_returns, portfolio_value_millions = 1, p = 0.95)
risk_ab_hw <- hull_white_var_es(portfolio_ab_returns, portfolio_value_millions = 2, p = 0.95)
question_2_table <- data.frame(
portfolio = c("Asset A only", "Asset B only", "A and B combined"),
VaR_millions = c(risk_a_hw$VaR, risk_b_hw$VaR, risk_ab_hw$VaR)
)
kable(
question_2_table %>% mutate(across(where(is.numeric), ~ round(.x, 6))),
caption = "Hull-White 95% VaR results for Question 2"
)
Hull-White 95% VaR results for Question 2
| Asset A only |
0.014988 |
| Asset B only |
0.050399 |
| A and B combined |
0.057150 |
comparison <- data.frame(
lhs = risk_a_hw$VaR + risk_b_hw$VaR,
rhs = risk_ab_hw$VaR,
lhs_less_than_rhs = (risk_a_hw$VaR + risk_b_hw$VaR) < risk_ab_hw$VaR
)
kable(
comparison %>% mutate(across(where(is.numeric), ~ round(.x, 6))),
caption = "Check of whether VaR(A) + VaR(B) < VaR(A+B)"
)
Check of whether VaR(A) + VaR(B) < VaR(A+B)
| 0.065387 |
0.05715 |
FALSE |
TODO: Briefly comment on whether diversification reduces measured
risk in your data.
3. Portfolio Composition Decision
3(a) Minimum-Risk Portfolio With A And B Or A And C
best_with_b <- two_asset_hull_white_search(
returns_xts = returns,
asset_a = asset_a,
asset_other = asset_b,
min_weight_a = 0.2,
p = 0.95,
portfolio_value_millions = 2
)
best_with_c <- two_asset_hull_white_search(
returns_xts = returns,
asset_a = asset_a,
asset_other = asset_c,
min_weight_a = 0.2,
p = 0.95,
portfolio_value_millions = 2
)
comparison_3a <- bind_rows(
cbind(pair = paste(asset_a, "+", asset_b), best_with_b),
cbind(pair = paste(asset_a, "+", asset_c), best_with_c)
)
kable(
comparison_3a %>% mutate(across(where(is.numeric), ~ round(.x, 6))),
caption = "Best two-asset portfolios subject to the 20% Asset A constraint"
)
Best two-asset portfolios subject to the 20% Asset A
constraint
| SPY + GLD |
1.00 |
0.00 |
0.029976 |
0.042522 |
0.001247 |
| SPY + TLT |
0.35 |
0.65 |
0.015983 |
0.021381 |
0.000301 |
TODO: State which pairing produces the lower 95% Hull-White ES and
explain why.
3(b) Expected Profit Versus The Higher-Return Single Asset
asset_means <- data.frame(
asset = colnames(returns),
mean_daily_return = colMeans(returns)
)
best_single_asset <- asset_means %>%
arrange(desc(mean_daily_return)) %>%
slice(1) %>%
mutate(expected_daily_profit_millions = 2 * mean_daily_return)
kable(
asset_means %>% mutate(across(where(is.numeric), ~ round(.x, 6))),
caption = "Average simple daily returns by asset"
)
Average simple daily returns by asset
| SPY |
SPY |
0.000624 |
| GLD |
GLD |
0.000821 |
| TLT |
TLT |
-0.000104 |
kable(
best_single_asset %>% mutate(across(where(is.numeric), ~ round(.x, 6))),
caption = "Higher-return single-asset alternative for GBP 2 million"
)
Higher-return single-asset alternative for GBP 2
million
| GLD |
GLD |
0.000821 |
0.001641 |
TODO: Compare the expected profit of your minimum-risk portfolio
against the all-in higher-return alternative, and comment on whether the
trade-off feels worthwhile.
Final Checks
- Confirm that your dates end on 17 February 2026.
- Confirm that the report contains your own discussion, not only
output tables.
- If you want reproducible results, save the downloaded data and rerun
from the saved files.
- If your lecturer requires PDF submission, knit to the required
format before submitting.