Model: \(R_i - R_f = \alpha + \beta(R_m - R_f) + \varepsilon\)
Given:
| Term | Estimate | Std. Error |
|---|---|---|
| Intercept (\(\alpha\)) | 0.0017 | 0.0020 |
| Market premium (\(\beta\)) | 0.98 | 0.17 |
\(R^2 = 0.50\), \(E[R_m - R_f] = 0.70\%\), \(n = 96\) months, critical \(|t| \approx 1.98\).
beta_hat <- 0.98
se_beta <- 0.17
t_beta <- beta_hat / se_beta
t_crit <- 1.98
cat("t-statistic for beta:", round(t_beta, 4), "\n")t-statistic for beta: 5.7647
Critical |t|: 1.98
cat("Decision:", ifelse(abs(t_beta) > t_crit, "Reject H0: beta = 0", "Fail to reject H0: beta = 0"), "\n")Decision: Reject H0: beta = 0
Formula: \[t_{\hat{\beta}} = \frac{\hat{\beta}}{SE(\hat{\beta})} = \frac{0.98}{0.17} = 5.7647\]
Decision: \(|5.7647| > 1.98\) → Reject \(H_0: \beta = 0\) at the 5% significance level.
Economic interpretation: \(\hat{\beta} \approx 0.98\) means the fund’s excess return moves almost one-for-one with the market risk premium. A 1% increase in the market excess return is associated with approximately a 0.98% increase in the fund’s excess return. The fund has near-market systematic risk.
t-statistic for H0: beta = 1: -0.1176
cat("Decision:", ifelse(abs(t_beta1) > t_crit, "Reject H0: beta = 1", "Fail to reject H0: beta = 1"), "\n")Decision: Fail to reject H0: beta = 1
Formula: \[t = \frac{\hat{\beta} - 1}{SE(\hat{\beta})} = \frac{0.98 - 1}{0.17} = \frac{-0.02}{0.17} = -0.1176\]
Decision: \(|-0.1176| < 1.98\) → Fail to reject \(H_0: \beta = 1\) at the 5% level.
Interpretation: The fund’s systematic risk is statistically indistinguishable from the market (\(\beta = 1\)). It is neither a leveraged (\(\beta > 1\)) nor a defensive (\(\beta < 1\)) fund. Its market exposure is essentially equivalent to a passive index position.
alpha_hat <- 0.0017
se_alpha <- 0.0020
t_alpha <- alpha_hat / se_alpha
cat("Jensen's alpha:", alpha_hat, "\n")Jensen's alpha: 0.0017
t-statistic for alpha: 0.85
cat("Decision:", ifelse(abs(t_alpha) > t_crit, "Reject H0: alpha = 0", "Fail to reject H0: alpha = 0"), "\n")Decision: Fail to reject H0: alpha = 0
Formula: \[t_{\hat{\alpha}} = \frac{\hat{\alpha}}{SE(\hat{\alpha})} = \frac{0.0017}{0.0020} = 0.8500\]
Decision: \(|0.8500| < 1.98\) → Fail to reject \(H_0: \alpha = 0\) at the 5% level.
Conclusion: The data do not statistically justify the marketing claim of “positive risk-adjusted performance.” Although the point estimate \(\hat{\alpha} = 0.0017\) (0.17% per month) is positive, the t-statistic of 0.85 corresponds to a p-value of approximately 0.40, meaning there is a 40% probability of observing this result by chance even if true \(\alpha = 0\). The claim is statistically unwarranted.
50% of the monthly variation in the fund’s excess return is explained by market movements — this is the systematic (non-diversifiable) component of risk.
The remaining 50% is idiosyncratic (diversifiable) risk — return variation specific to the fund’s individual holdings, unrelated to market-wide movements. A fully diversified investor would not be compensated for bearing this component.
\[R^2 = \frac{\text{Systematic variance}}{\text{Total variance}} = 0.50\]
E_mkt_premium <- 0.0070 # 0.70% expressed as decimal
E_fund_excess <- beta_hat * E_mkt_premium
cat("CAPM-implied expected monthly excess return:", round(E_fund_excess * 100, 4), "%\n")CAPM-implied expected monthly excess return: 0.686 %
Formula: \[E[R_i - R_f] = \hat{\beta} \times E[R_m - R_f] = 0.98 \times 0.70\% = \mathbf{0.6860\%}\]
Model: \(R_i - R_f = \alpha + b \cdot MKT + s \cdot SMB + h \cdot HML + \varepsilon\)
Given (\(n = 144\) months):
| Term | Estimate | Std. Error |
|---|---|---|
| Intercept (\(\alpha\)) | 0.0029 | 0.0018 |
| MKT (\(b\)) | 0.97 | 0.08 |
| SMB (\(s\)) | 0.75 | 0.11 |
| HML (\(h\)) | −0.13 | 0.13 |
\(R^2 = 0.92\), Adjusted \(R^2 = 0.918\), critical \(|t| \approx 1.98\).
coefs <- data.frame(
Term = c("alpha (intercept)", "b (MKT)", "s (SMB)", "h (HML)"),
Estimate = c(0.0029, 0.97, 0.75, -0.13),
Std_Error = c(0.0018, 0.08, 0.11, 0.13)
)
coefs$t_stat <- round(coefs$Estimate / coefs$Std_Error, 4)
coefs$Significant <- ifelse(abs(coefs$t_stat) > 1.98, "Yes (5%)", "No")
print(coefs, row.names = FALSE) Term Estimate Std_Error t_stat Significant
alpha (intercept) 0.0029 0.0018 1.6111 No
b (MKT) 0.9700 0.0800 12.1250 Yes (5%)
s (SMB) 0.7500 0.1100 6.8182 Yes (5%)
h (HML) -0.1300 0.1300 -1.0000 No
Formulas: \[t_\alpha = \frac{0.0029}{0.0018} = 1.6111 \quad t_b = \frac{0.97}{0.08} = 12.1250 \quad t_s = \frac{0.75}{0.11} = 6.8182 \quad t_h = \frac{-0.13}{0.13} = -1.0000\]
Significant at 5%: \(b\) (MKT) and \(s\) (SMB) only. \(\alpha\) and \(h\) (HML) are not significant.
s_hat <- 0.75
h_hat <- -0.13
cat("SMB loading (s):", s_hat, "→", ifelse(s_hat > 0, "Small-cap tilt", "Large-cap tilt"), "\n")SMB loading (s): 0.75 → Small-cap tilt
cat("HML loading (h):", h_hat, "→", ifelse(h_hat < 0, "Growth tilt", "Value tilt"),
"(not significant)\n")HML loading (h): -0.13 → Growth tilt (not significant)
Overall style: Small-cap blend / slight growth tilt
Overall classification: Small-cap blend (no statistically significant value/growth tilt).
alpha_ff <- 0.0029
se_alpha_ff <- 0.0018
t_alpha_ff <- alpha_ff / se_alpha_ff
cat("FF alpha:", alpha_ff, "(", round(alpha_ff * 100, 2), "% per month )\n")FF alpha: 0.0029 ( 0.29 % per month )
t-statistic: 1.6111
cat("Decision:", ifelse(abs(t_alpha_ff) > 1.98, "Significant — manager adds value",
"Not significant — no evidence of value-added"), "\n")Decision: Not significant — no evidence of value-added
\(\hat{\alpha} = 0.0029\) (0.29% per month). With \(t = 1.61 < 1.98\), this is not statistically significant at the 5% level.
Conclusion: After accounting for exposure to the market, size, and value factors, there is no statistically reliable evidence that the manager generates returns beyond what the three systematic factors predict. The positive point estimate may be due to sampling variation. The manager does not demonstrably add value beyond factor exposures.
R2_capm <- 0.75
R2_ff <- 0.92
adj_R2 <- 0.918
n <- 144
k_capm <- 1 # one predictor
k_ff <- 3 # three predictors
adj_R2_capm_calc <- 1 - (1 - R2_capm) * (n - 1) / (n - k_capm - 1)
adj_R2_ff_calc <- 1 - (1 - R2_ff) * (n - 1) / (n - k_ff - 1)
cat("CAPM R²:", R2_capm, "| Approx Adj R²:", round(adj_R2_capm_calc, 4), "\n")CAPM R²: 0.75 | Approx Adj R²: 0.7482
FF R²: 0.92 | Approx Adj R²: 0.9183
Rise in R²: 0.17
What the rise indicates: Adding SMB and HML increases \(R^2\) from 0.75 to 0.92 — an additional 17% of the fund’s return variation is explained. This gain is almost entirely attributable to the fund’s significant small-cap loading (\(s = 0.75\)); the fund’s returns behave like those of small-cap stocks, a dimension the single-factor CAPM ignores.
Why adjusted \(R^2\) is appropriate for model comparison:
\[\bar{R}^2 = 1 - \frac{(1-R^2)(n-1)}{n - k - 1}\]
Adding any predictor — even a pure noise variable — mechanically increases \(R^2\). Adjusted \(R^2\) penalizes for additional parameters; it rises only if the new predictor improves fit by more than chance would. When comparing models with different numbers of predictors (1 vs. 3 factors here), adjusted \(R^2\) is the appropriate metric to confirm the improvement is genuine.
Model: \(\text{logit}\, P(\text{Up}) = \beta_0 + \beta_1 r_{t-1} + \beta_2 \Delta VIX_{t-1}\)
Coefficients: \(\beta_0 = -0.02\), \(\beta_1 = 5.4\), \(\beta_2 = -0.38\). Inputs: \(r_{t-1} = 0.010\), \(\Delta VIX = 1.5\).
b0 <- -0.02; b1 <- 5.4; b2 <- -0.38
r_lag <- 0.010; delta_vix <- 1.5
logit_val <- b0 + b1 * r_lag + b2 * delta_vix
cat("logit =", b0, "+", b1, "*", r_lag, "+", b2, "*", delta_vix, "\n")logit = -0.02 + 5.4 * 0.01 + -0.38 * 1.5
logit = -0.536
prob_up <- 1 / (1 + exp(-logit_val))
cat("P(Up) = 1 / (1 + exp(", round(-logit_val, 4), ")) =", round(prob_up, 4), "\n")P(Up) = 1 / (1 + exp( 0.536 )) = 0.3691
Predicted class (threshold 0.5): Down
Formula: \[\text{logit} = -0.02 + 5.4(0.010) + (-0.38)(1.5) = -0.02 + 0.054 - 0.570 = -0.5360\]
\[P(\text{Up}) = \frac{1}{1 + e^{0.5360}} = \frac{1}{1 + 1.7096} = 0.3693\]
Since \(0.3693 < 0.50\) → Predicted class: Down
\(\beta_1 = +5.4\) (lagged return): A positive lagged market return increases the log-odds of an “Up” day. This captures short-term momentum — a market that rose yesterday is more likely to rise tomorrow. The large magnitude (5.4) indicates the effect is economically meaningful.
\(\beta_2 = -0.38\) (\(\Delta\)VIX): A rise in implied volatility (the “fear index”) decreases the probability of an “Up” day. This reflects the well-documented negative volatility–return relationship: when uncertainty spikes, risk-averse investors sell, driving prices down. A 1-unit rise in VIX reduces the log-odds by 0.38.
TP <- 67; FP <- 44; FN <- 33; TN <- 56
N <- TP + FP + FN + TN
accuracy <- (TP + TN) / N
sensitivity <- TP / (TP + FN) # True Positive Rate / Recall
specificity <- TN / (TN + FP)
precision <- TP / (TP + FP)
cat("Confusion Matrix:\n")Confusion Matrix:
Actual Up Actual Down
Predicted Up 67 (TP) 44 (FP)
Predicted Down 33 (FN) 56 (TN)
Accuracy = ( 67 + 56 ) / 200 = 0.615
Sensitivity = 67 / ( 67 + 33 ) = 0.67
Specificity = 56 / ( 56 + 44 ) = 0.56
Precision = 67 / ( 67 + 44 ) = 0.6036
Formulas:
\[\text{Accuracy} = \frac{TP + TN}{N} = \frac{67 + 56}{200} = 0.6150\]
\[\text{Sensitivity} = \frac{TP}{TP + FN} = \frac{67}{100} = 0.6700\]
\[\text{Specificity} = \frac{TN}{TN + FP} = \frac{56}{100} = 0.5600\]
\[\text{Precision} = \frac{TP}{TP + FP} = \frac{67}{111} = 0.6036\]
# Dataset is balanced: 100 Up, 100 Down
naive_accuracy <- 100 / 200
model_accuracy <- (TP + TN) / N
cat("Naive majority-class accuracy:", naive_accuracy, "\n")Naive majority-class accuracy: 0.5
Model accuracy: 0.615
Model beats naive benchmark: TRUE
Naive accuracy: The dataset is balanced (100 Up, 100 Down), so the majority class is either. Predicting the majority class always gives accuracy = \(100/200 = \mathbf{0.50}\).
Model accuracy = 0.615 > 0.50 → The model beats the naive benchmark.
Why accuracy alone is inadequate for a trading system: In trading, false positives (predicting Up when the market falls) and false negatives (predicting Down when the market rises) carry asymmetric economic costs depending on position sizing, transaction costs, and risk tolerance. Accuracy treats all errors equally, ignoring P&L consequences. For example, a model that is always right on small Up days but always wrong on large Down days may have high accuracy but negative expected P&L.
A more economically relevant criterion: The Sharpe ratio (or information ratio) of the resulting trading strategy’s P&L — this directly measures risk-adjusted profitability. Alternatively, for a long-only strategy, Precision is key: it measures what fraction of “Up” predictions actually result in gains.
Strategy: \(\bar{r} = 0.70\%\) per month, \(s = 5.50\%\) per month, \(n = 48\) months.
r_bar <- 0.0070 # 0.70%
s_r <- 0.0550 # 5.50%
n_obs <- 48
SR_monthly <- r_bar / s_r
scale <- sqrt(12) # annualisation scaling factor
SR_annual <- SR_monthly * scale
cat("Monthly Sharpe ratio = ", round(r_bar * 100, 2), "% /", round(s_r * 100, 2), "%\n")Monthly Sharpe ratio = 0.7 % / 5.5 %
= 0.1273
Scaling factor = sqrt(12) = 3.4641
Annualized Sharpe = 0.1273 x 3.4641
= 0.4409
Formulas:
\[SR_{\text{monthly}} = \frac{\bar{r}}{s} = \frac{0.0070}{0.0550} = 0.1273\]
\[SR_{\text{annual}} = SR_{\text{monthly}} \times \sqrt{12} = 0.1273 \times 3.4641 = \mathbf{0.4408}\]
Scaling factor justification: Under i.i.d. returns, \(\sigma^2_{\text{annual}} = 12 \cdot \sigma^2_{\text{monthly}}\), so \(\sigma_{\text{annual}} = \sqrt{12} \cdot \sigma_{\text{monthly}}\). Since the mean also scales by 12, the Sharpe ratio scales by \(12 / \sqrt{12} = \sqrt{12}\).
# Illustration of the iid bootstrap procedure
set.seed(42)
# Simulate the 48 monthly returns for demonstration
set.seed(42)
r_sim <- rnorm(48, mean = 0.007, sd = 0.055)
B <- 10000
sr_boot <- replicate(B, {
r_b <- sample(r_sim, size = 48, replace = TRUE)
mean(r_b) / sd(r_b)
})
cat("Bootstrap SE of monthly Sharpe ratio (iid, B =", B, "):", round(sd(sr_boot), 4), "\n")Bootstrap SE of monthly Sharpe ratio (iid, B = 10000 ): 0.149
cat("95% Bootstrap CI: [", round(quantile(sr_boot, 0.025), 4), ",",
round(quantile(sr_boot, 0.975), 4), "]\n\n")95% Bootstrap CI: [ -0.2097 , 0.3765 ]
NOTE: This is illustrative. The iid bootstrap is biased for autocorrelated returns.
Use the stationary (block) bootstrap for valid inference.
Bootstrap procedure (step-by-step):
Why ordinary i.i.d. bootstrap is inappropriate: Monthly financial returns typically exhibit serial autocorrelation (momentum, mean-reversion) and volatility clustering (GARCH effects). The i.i.d. bootstrap samples observations independently, thereby destroying the time-series dependence structure and producing an underestimated standard error.
Fix — Block Bootstrap (stationary bootstrap): The stationary bootstrap (Politis & Romano, 1994) samples contiguous blocks of length drawn from a geometric distribution, preserving local autocorrelation. Alternatively, the moving block bootstrap uses fixed-length blocks of size \(l \approx \sqrt{n} \approx 7\) months. Either variant respects the temporal dependence in the return series.
lambda_min <- 0.030; factors_min <- 14
lambda_1se <- 0.065; factors_1se <- 7
cat("Lambda (min CV error): ", lambda_min, "| Factors retained:", factors_min, "\n")Lambda (min CV error): 0.03 | Factors retained: 14
Lambda (1-SE rule): 0.065 | Factors retained: 7
Recommended: lambda = 0.065 (1-SE rule)
Reason: parsimony reduces overfitting with 48 months of data and 60 candidate factors.
Recommendation: Deploy \(\lambda = 0.065\) (the 1-SE rule, retaining 7 factors).
Justification:
Walk-forward cross-validation scheme:
Step 1: Order all T observations chronologically: t = 1, 2, ..., T
Step 2: Set initial training window (e.g., first 24 months)
Step 3: Train model on t = 1..24; predict and record OOS metric at t = 25
Step 4: Expand window by one month; retrain on t = 1..25; predict t = 26
Step 5: Repeat until t = T; average all OOS metrics
Why k-fold CV is unsafe for financial time series:
- k-fold randomly shuffles data into folds
- Future data can appear in training set → look-ahead bias
- OOS estimates are optimistically biased and not tradeable in practice
Walk-forward scheme (expanding window):
| Step | Training Set | Test Point |
|---|---|---|
| 1 | \(t = 1 \ldots 24\) | \(t = 25\) |
| 2 | \(t = 1 \ldots 25\) | \(t = 26\) |
| \(\vdots\) | \(\vdots\) | \(\vdots\) |
| \(T-24\) | \(t = 1 \ldots T-1\) | \(t = T\) |
The model is retrained at each step with all available historical data, and the test point always lies strictly in the future relative to the training data.
Why standard \(k\)-fold CV is unsafe:
Standard \(k\)-fold randomly assigns observations to folds, so observations from \(t = 40\) may appear in the training set while \(t = 20\) appears in the test set. This constitutes look-ahead bias — the model is trained on information it could not have had at prediction time in real deployment. The resulting OOS metrics are spuriously optimistic and do not reflect true out-of-sample performance.
Walk-forward CV strictly preserves temporal ordering, mimicking live trading conditions: the model only ever uses past data to predict the future, exactly as it would in practice.
End of examination.