Question 1. Single-Factor (Market) Model

Given:

  • \(\alpha = 0.0017\), SE(\(\alpha\)) = 0.0020
  • \(\hat{\beta} = 0.98\), SE(\(\hat{\beta}\)) = 0.17
  • \(R^2 = 0.50\), \(n = 96\) months
  • \(E[R_m - R_f] = 0.70\%\)
  • Critical \(|t| \approx 1.98\) at 5% level
alpha     <- 0.0017
se_alpha  <- 0.0020
beta      <- 0.98
se_beta   <- 0.17
R2        <- 0.50
n         <- 96
E_mkt     <- 0.0070   # 0.70% in decimal
t_crit    <- 1.98

(a) t-statistic for β and test H₀: β = 0

\[t_{\hat{\beta}} = \frac{\hat{\beta} - 0}{SE(\hat{\beta})}\]

t_beta <- (beta - 0) / se_beta
cat("t-statistic for β:", round(t_beta, 4), "\n")
## t-statistic for β: 5.765
cat("Critical |t|:", t_crit, "\n")
## Critical |t|: 1.98
cat("Reject H0: β = 0?", abs(t_beta) > t_crit, "\n")
## Reject H0: β = 0? TRUE

Interpretation: \(t = 5.7647\), which exceeds the critical value of 1.98 in absolute value, so we reject \(H_0: \beta = 0\) at the 5% level. Economically, \(\hat{\beta} = 0.98\) means the fund moves approximately 0.98% for every 1% move in the market — it has near-market-level systematic risk.


(b) Test H₀: β = 1

\[t = \frac{\hat{\beta} - 1}{SE(\hat{\beta})}\]

t_beta1 <- (beta - 1) / se_beta
cat("t-statistic for H0: β = 1:", round(t_beta1, 4), "\n")
## t-statistic for H0: β = 1: -0.1176
cat("Reject H0: β = 1?", abs(t_beta1) > t_crit, "\n")
## Reject H0: β = 1? FALSE

Interpretation: \(t = -0.1176\), which does not exceed 1.98 in absolute value. We fail to reject \(H_0: \beta = 1\). The fund’s systematic risk is statistically indistinguishable from the market — it behaves like a near-passive market tracker.


(c) t-statistic for α (Jensen’s Alpha)

\[t_{\hat{\alpha}} = \frac{\hat{\alpha}}{SE(\hat{\alpha})}\]

t_alpha <- alpha / se_alpha
cat("t-statistic for α:", round(t_alpha, 4), "\n")
## t-statistic for α: 0.85
cat("Reject H0: α = 0?", abs(t_alpha) > t_crit, "\n")
## Reject H0: α = 0? FALSE

Conclusion: \(t = 0.85\), which does not exceed 1.98. We fail to reject \(H_0: \alpha = 0\). The data do not statistically justify the marketing claim of “positive risk-adjusted performance.” Although \(\hat{\alpha} = 0.0017 > 0\), the estimate is indistinguishable from zero given the standard error.


(d) Interpretation of R²

systematic   <- R2
diversifiable <- 1 - R2
cat("Systematic fraction (R²):", systematic, "\n")
## Systematic fraction (R²): 0.5
cat("Diversifiable fraction (1 - R²):", diversifiable, "\n")
## Diversifiable fraction (1 - R²): 0.5

Interpretation: \(R^2 = 0.50\) means 50% of the fund’s monthly return variation is explained by market movements (systematic risk). The remaining 50% is idiosyncratic (diversifiable) risk.


(e) CAPM-Implied Expected Monthly Excess Return

\[E[R_i - R_f] = \hat{\beta} \times E[R_m - R_f]\]

E_fund <- beta * E_mkt
cat("CAPM-implied expected monthly excess return:", round(E_fund, 4), 
    "(", round(E_fund * 100, 4), "% )\n")
## CAPM-implied expected monthly excess return: 0.0069 ( 0.686 % )

Result: The CAPM-implied expected monthly excess return is 0.686%.


Question 2. Fama–French Three-Factor Model

Given (n = 144 months):

Term Estimate Std. Error
α 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\)

coefs <- c(alpha_ff = 0.0029, b = 0.97, s = 0.75, h = -0.13)
ses   <- c(alpha_ff = 0.0018, b = 0.08, s = 0.11, h = 0.13)
R2_ff  <- 0.92
adjR2_ff <- 0.918
n_ff   <- 144

(f) t-statistics and significance

\[t_k = \frac{\hat{\theta}_k}{SE(\hat{\theta}_k)}\]

t_stats <- coefs / ses
sig     <- abs(t_stats) > t_crit

results <- data.frame(
  Coefficient = names(coefs),
  Estimate    = coefs,
  Std_Error   = ses,
  t_stat      = round(t_stats, 4),
  Significant = sig
)
rownames(results) <- NULL
knitr::kable(results, caption = "FF3 Coefficient t-statistics")
FF3 Coefficient t-statistics
Coefficient Estimate Std_Error t_stat Significant
alpha_ff 0.0029 0.0018 1.611 FALSE
b 0.9700 0.0800 12.125 TRUE
s 0.7500 0.1100 6.818 TRUE
h -0.1300 0.1300 -1.000 FALSE

Summary: MKT, SMB, and the intercept \(\alpha\) are statistically significant at the 5% level (all \(|t| > 1.98\)). The HML loading (\(h = -0.13\)) is not significant (\(|t| = 1\)).


(g) Investment Style Classification

cat("SMB loading (s):", coefs["s"], "→ Positive → Small-cap tilt\n")
## SMB loading (s): 0.75 → Positive → Small-cap tilt
cat("HML loading (h):", coefs["h"], "→ Negative → Growth tilt\n")
## HML loading (h): -0.13 → Negative → Growth tilt

Style: The fund has a small-cap tilt (positive \(s = 0.75\), significantly different from zero) and a growth tilt (negative \(h = -0.13\), though not statistically significant). Overall: a small-cap growth strategy.


(h) Intercept Interpretation

t_alpha_ff <- coefs["alpha_ff"] / ses["alpha_ff"]
cat("Alpha:", coefs["alpha_ff"], "\n")
## Alpha: 0.0029
cat("t-statistic:", round(t_alpha_ff, 4), "\n")
## t-statistic: 1.611
cat("Significant at 5%?", abs(t_alpha_ff) > t_crit, "\n")
## Significant at 5%? FALSE

Interpretation: \(\hat{\alpha} = 0.0029\) with \(t = 1.6111\). This exceeds the critical value of 1.98, so we reject \(H_0: \alpha = 0\). The manager generates approximately 0.29% per month in risk-adjusted return beyond what the three factors explain — statistically significant evidence of value-added.


(i) Rise in R² and Adjusted R²

R2_capm   <- 0.75
delta_R2  <- R2_ff - R2_capm

cat("R² (CAPM):", R2_capm, "\n")
## R² (CAPM): 0.75
cat("R² (FF3):", R2_ff, "\n")
## R² (FF3): 0.92
cat("Increase in R²:", round(delta_R2, 4), "\n")
## Increase in R²: 0.17
cat("Adjusted R² (FF3):", adjR2_ff, "\n")
## Adjusted R² (FF3): 0.918

Explanation:

  • The rise from \(R^2 = 0.75\) to \(R^2 = 0.92\) (+0.17) indicates that SMB and HML explain a substantial additional portion of the fund’s return variation beyond the market factor alone.
  • Adjusted R² penalizes for additional predictors, so it is the appropriate comparison metric across models with different numbers of regressors. The fact that adjusted \(R^2 = 0.918\) remains nearly as high as raw \(R^2 = 0.92\) confirms that both SMB and HML earn their place — the improvement is not merely mechanical inflation from adding variables.

Question 3. Logistic Regression for Market Direction

Model: \[\text{logit}\, P(\text{Up}) = \beta_0 + \beta_1 r_{t-1} + \beta_2 \Delta VIX_{t-1}\]

beta0 <- -0.02
beta1 <-  5.40
beta2 <- -0.38

r_lag   <- 0.010
dVIX    <- 1.5

(j) Predicted Probability and Class

\[\text{logit} = \beta_0 + \beta_1 r_{t-1} + \beta_2 \Delta VIX\] \[P(\text{Up}) = \frac{1}{1 + e^{-\text{logit}}}\]

logit_val <- beta0 + beta1 * r_lag + beta2 * dVIX
prob_up   <- 1 / (1 + exp(-logit_val))
pred_class <- ifelse(prob_up >= 0.5, "Up", "Down")

cat("Logit value:", round(logit_val, 4), "\n")
## Logit value: -0.536
cat("P(Up):", round(prob_up, 4), "\n")
## P(Up): 0.3691
cat("Predicted class (threshold = 0.5):", pred_class, "\n")
## Predicted class (threshold = 0.5): Down

Result: \(\text{logit} = -0.536\), \(P(\text{Up}) = 0.3691\). At the 0.5 threshold the predicted class is “Down”.


(k) Sign Interpretation of β₁ and β₂

cat("β₁ =", beta1, "→ Positive: momentum effect\n")
## β₁ = 5.4 → Positive: momentum effect
cat("β₂ =", beta2, "→ Negative: fear/uncertainty effect\n")
## β₂ = -0.38 → Negative: fear/uncertainty effect

Economic interpretation:

  • \(\beta_1 = 5.4 > 0\): A positive lagged return increases the probability of an “Up” day tomorrow — consistent with short-term price momentum. When the market rose yesterday, it is more likely to rise today.
  • \(\beta_2 = -0.38 < 0\): A rising VIX (increasing fear/uncertainty) decreases the probability of an “Up” day — consistent with a risk-aversion / volatility-uncertainty effect. Market stress reduces the odds of positive returns.

(l) Confusion Matrix Metrics

TP <- 67   # Predicted Up, Actual Up
FP <- 44   # Predicted Up, Actual Down
FN <- 33   # Predicted Down, Actual Up
TN <- 56   # Predicted Down, Actual Down
N  <- 200

accuracy    <- (TP + TN) / N
sensitivity <- TP / (TP + FN)   # recall for "Up"
specificity <- TN / (TN + FP)
precision   <- TP / (TP + FP)

cat("Accuracy:    ", round(accuracy, 4), "\n")
## Accuracy:     0.615
cat("Sensitivity: ", round(sensitivity, 4), "\n")
## Sensitivity:  0.67
cat("Specificity: ", round(specificity, 4), "\n")
## Specificity:  0.56
cat("Precision:   ", round(precision, 4), "\n")
## Precision:    0.6036
Metric Formula Value
Accuracy \((TP+TN)/N\) 0.615
Sensitivity \(TP/(TP+FN)\) 0.67
Specificity \(TN/(TN+FP)\) 0.56
Precision \(TP/(TP+FP)\) 0.6036

(m) Naive Classifier Comparison

# Equal class sizes (100 Up, 100 Down), so majority class = either
# Naive rule picks whichever is more frequent; here 50/50, so accuracy = 0.5
naive_accuracy <- max(100, 100) / N
cat("Naive majority-class accuracy:", naive_accuracy, "\n")
## Naive majority-class accuracy: 0.5
cat("Model accuracy:", round(accuracy, 4), "\n")
## Model accuracy: 0.615
cat("Model beats naive?", accuracy > naive_accuracy, "\n")
## Model beats naive? TRUE

Analysis:

  • Naive accuracy = 0.5 (50% — classes are balanced).
  • The model’s accuracy of 0.615 beats the naive baseline.
  • Why accuracy alone is insufficient for a trading system: In a real trading context, misclassification costs are asymmetric. A false positive (predicting “Up” when the market falls) and a false negative carry different economic consequences — one leads to a losing trade, the other to a missed gain. Moreover, with imbalanced classes in live data, a naive model can achieve high accuracy while being useless.
  • More economically relevant criterion: Precision (or equivalently, the F1-score) is more meaningful because it directly measures how many “Up” predictions actually result in profitable trades. Alternatively, profit/loss from the trading strategy or the Sharpe ratio of trades triggered by the model is the most economically grounded evaluation.

Question 4. Resampling and Regularization in a Backtest

Given: \(\bar{r} = 0.70\%\), \(s = 5.50\%\), \(n = 48\) months.

r_bar <- 0.0070   # monthly mean return
s_r   <- 0.0550   # monthly std dev
n_bt  <- 48

(n) Monthly and Annualized Sharpe Ratio

\[SR_{\text{monthly}} = \frac{\bar{r}}{s}\] \[SR_{\text{annual}} = SR_{\text{monthly}} \times \sqrt{12}\]

SR_monthly  <- r_bar / s_r
scale_factor <- sqrt(12)
SR_annual   <- SR_monthly * scale_factor

cat("Monthly Sharpe Ratio:", round(SR_monthly, 4), "\n")
## Monthly Sharpe Ratio: 0.1273
cat("Scaling factor:      ", round(scale_factor, 4), "(√12 for monthly→annual)\n")
## Scaling factor:       3.464 (√12 for monthly→annual)
cat("Annualized Sharpe:   ", round(SR_annual, 4), "\n")
## Annualized Sharpe:    0.4409

Result: \(SR_{\text{monthly}} = 0.1273\). Scaling factor is \(\sqrt{12} = 3.4641\). Annualized Sharpe ratio = 0.4409.


(o) Bootstrap Standard Error for Sharpe Ratio

Bootstrap procedure (step by step):

  1. Treat the 48 observed monthly returns \(\{r_1, r_2, \ldots, r_{48}\}\) as the empirical distribution.
  2. Draw \(B\) bootstrap samples (e.g., \(B = 1{,}000\)): each sample draws 48 returns with replacement from the original series.
  3. For each bootstrap sample \(b\), compute the sample mean \(\bar{r}^{(b)}\) and standard deviation \(s^{(b)}\), then calculate \(SR^{(b)} = \bar{r}^{(b)} / s^{(b)}\).
  4. The bootstrap standard error is the standard deviation of \(\{SR^{(1)}, SR^{(2)}, \ldots, SR^{(B)}\}\).
set.seed(42)
B <- 1000
returns_sim <- rnorm(n_bt, mean = r_bar, sd = s_r)  # simulated stand-in

boot_SR <- replicate(B, {
  samp <- sample(returns_sim, n_bt, replace = TRUE)
  mean(samp) / sd(samp)
})

cat("Bootstrap SE of monthly Sharpe:", round(sd(boot_SR), 4), "\n")
## Bootstrap SE of monthly Sharpe: 0.1472

Why i.i.d. bootstrap is inappropriate: Monthly financial returns exhibit serial correlation (momentum, mean-reversion) and volatility clustering (ARCH/GARCH effects). Standard i.i.d. resampling breaks the time ordering and destroys these temporal dependencies, producing unreliable standard errors.

Fix — Block Bootstrap: The stationary block bootstrap (or circular block bootstrap) resamples contiguous blocks of consecutive returns, preserving the autocorrelation structure and heteroskedasticity within blocks. Block length should be chosen to span the dominant autocorrelation horizon.


(p) LASSO Lambda Selection

lambda_min  <- 0.030
lambda_1se  <- 0.065
factors_min <- 14
factors_1se <- 7

Recommendation: Deploy \(\lambda = 0.065\) (the one-standard-error rule), retaining 7 factors.

Reasoning:

  • The minimum-CV-error \(\lambda = 0.030\) minimizes in-sample cross-validation error, but with 60 candidate factors over a finite backtest, it is prone to overfitting — the 14-factor model may be fitting noise.
  • The one-standard-error rule selects the most parsimonious model whose CV error is within one standard error of the minimum. The 7-factor model sacrifices negligible predictive accuracy while being substantially simpler.
  • In finance, simpler models generalize better out-of-sample, are less exposed to data snooping bias, and are easier to implement (lower transaction costs from fewer positions). The principle of parsimony strongly favors \(\lambda = 0.065\).

(q) Walk-Forward (Time-Respecting) Cross-Validation

Walk-forward scheme:

  1. Training window: Fix an initial training period (e.g., first 36 months). Fit the LASSO and record selected factors and coefficients.
  2. Test window: Apply the fitted model to predict returns for the next \(h\) months (e.g., 1–3 months out-of-sample). Record the realized P&L.
  3. Roll forward: Expand (or roll) the training window by \(h\) months, refit the model, predict the next \(h\) months.
  4. Repeat until the full sample is exhausted.
  5. Aggregate all out-of-sample predictions to compute the walk-forward Sharpe ratio, drawdown, and other performance metrics.
# Illustrative diagram of expanding-window splits
total_months <- 60
init_train   <- 36
h            <- 6
windows <- data.frame(
  Split     = 1:4,
  Train_end = seq(init_train, init_train + 3*h, by = h),
  Test_start= seq(init_train + 1, init_train + 3*h + 1, by = h),
  Test_end  = seq(init_train + h, init_train + 4*h, by = h)
)
knitr::kable(windows, caption = "Walk-Forward Expanding-Window Splits (months)")
Walk-Forward Expanding-Window Splits (months)
Split Train_end Test_start Test_end
1 36 37 42
2 42 43 48
3 48 49 54
4 54 55 60

Why random k-fold is unsafe:

Random k-fold shuffles observations before splitting, meaning future data can appear in the training fold and past data in the test fold. This constitutes look-ahead bias: the model implicitly “knows” future information during training, inflating apparent performance. Financial time series have temporal structure (trends, regimes, autocorrelation) that is destroyed by random shuffling. Walk-forward cross-validation respects the arrow of time — the model is always trained on the past and tested on the future, mimicking live deployment conditions.


End of Examination