Q1. Single-Factor Model

The regression model is:

\[R_i - R_f = \alpha + \beta(R_m - R_f) + \varepsilon\]

Given info:

Part (a)

t-stat for beta:

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

b <- 0.98
se_b <- 0.17
t_b <- b / se_b
t_b
## [1] 5.764706

t = 5.7647. Since 5.7647 > 1.98, we reject H0: beta = 0. Beta is significant.

Economic meaning: beta = 0.98 means the fund’s excess return moves almost exactly with the market. A 1% rise in market excess return leads to roughly 0.98% rise in fund excess return. This fund has near-market-level systematic risk.

Part (b)

Test H0: beta = 1

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

t_b1 <- (b - 1) / se_b
t_b1
## [1] -0.1176471

t = -0.1176. Since |-0.1176| < 1.98, we fail to reject H0: beta = 1.

This means the fund’s systematic risk is not statistically different from the market. The beta could plausibly be exactly 1. In other words, the fund takes on roughly the same amount of market risk as a passive index fund.

Part (c)

Jensen’s alpha t-test:

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

a <- 0.0017
se_a <- 0.0020
t_a <- a / se_a
t_a
## [1] 0.85

t = 0.85 < 1.98, so alpha is NOT statistically significant. Even though alpha is positive (0.0017), we cannot say it’s different from zero with 95% confidence. The marketing team should NOT advertise “positive risk-adjusted performance” because the evidence does not statistically support that claim - it could just be noise.

Part (d)

R² = 0.50

R2 <- 0.50
cat("Systematic (explained by market):", R2, "\n")
## Systematic (explained by market): 0.5
cat("Idiosyncratic (diversifiable):", 1 - R2, "\n")
## Idiosyncratic (diversifiable): 0.5

50% of the variation in the fund’s returns is explained by market movements — this is the systematic portion. The remaining 50% is idiosyncratic (stock-specific) risk, which is diversifiable. Compared to a well-diversified fund (which would have R² close to 1), this fund has quite a lot of unsystematic risk.

Part (e)

CAPM expected return:

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

mkt_rp <- 0.0070
exp_ret <- b * mkt_rp
cat("Expected monthly excess return:", round(exp_ret, 4), "\n")
## Expected monthly excess return: 0.0069
cat("In percent:", round(exp_ret * 100, 4), "%\n")
## In percent: 0.686 %

The CAPM-implied expected monthly excess return for this fund is 0.686%.


Q2. Fama-French Three-Factor Model

Model:

\[R_i - R_f = \alpha + b \cdot MKT + s \cdot SMB + h \cdot HML + \varepsilon\]

Data from 144 months. Estimates:

Estimate SE
alpha 0.0029 0.0018
b (MKT) 0.97 0.08
s (SMB) 0.75 0.11
h (HML) -0.13 0.13

R² = 0.92, Adj. R² = 0.918

Part (f)

Compute all four t-statistics:

coefs <- c(alpha=0.0029, b=0.97, s=0.75, h=-0.13)
ses   <- c(alpha=0.0018, b=0.08, s=0.11, h=0.13)

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

data.frame(coef=coefs, se=ses, t=round(t_stats,4), significant=sig)
##          coef     se       t significant
## alpha  0.0029 0.0018  1.6111       FALSE
## b      0.9700 0.0800 12.1250        TRUE
## s      0.7500 0.1100  6.8182        TRUE
## h     -0.1300 0.1300 -1.0000       FALSE

Summary:

  • alpha: t = 1.6111 → not significant (p > 0.05)
  • MKT (b): t = 12.125 → significant
  • SMB (s): t = 6.8182 → significant
  • HML (h): t = -1 → not significant (p > 0.05)

Part (g)

Looking at the factor loadings:

cat("s (SMB loading):", coefs["s"], "\n")
## s (SMB loading): 0.75
cat("h (HML loading):", coefs["h"], "\n")
## h (HML loading): -0.13

SMB = +0.75 (positive, large, and significant): The fund loads heavily on the small-minus-big factor, meaning it leans toward small-cap stocks.

HML = -0.13 (negative, but not significant): The sign suggests a slight growth tilt (growth stocks tend to have low book-to-market, so negative HML loading means the fund holds more growth than value). But since this coefficient is insignificant, we shouldn’t overinterpret it — the fund is essentially style-neutral on the value/growth dimension.

Overall style: small-cap blend fund.

Part (h)

cat("alpha =", coefs["alpha"], "\n")
## alpha = 0.0029
cat("t-stat =", round(t_stats["alpha"], 4), "\n")
## t-stat = 1.6111
cat("Significant?", abs(t_stats["alpha"]) > 1.98, "\n")
## Significant? FALSE

The intercept (alpha) = 0.0029, meaning the fund earned an average of 0.29% per month beyond what the three factors explain. However, with t = 1.6111 < 1.98, this is not statistically significant. We cannot conclude that the manager genuinely adds value beyond the three systematic risk exposures (market, size, value). The outperformance may simply reflect luck.

Part (i)

cat("CAPM R2:      0.75\n")
## CAPM R2:      0.75
cat("FF3 R2:       0.92\n")
## FF3 R2:       0.92
cat("FF3 Adj. R2:  0.918\n")
## FF3 Adj. R2:  0.918
cat("Improvement:  0.92 - 0.75 =", 0.92 - 0.75, "\n")
## Improvement:  0.92 - 0.75 = 0.17

What the jump means: Adding SMB and HML factors increases R² from 0.75 to 0.92. This 17-percentage-point gain shows that a large part of what looked like “unexplained return variation” under CAPM is actually explained by the fund’s exposure to the size premium and value premium. The fund’s returns aren’t unusual — they’re mostly compensation for taking on small-cap risk.

Why adjusted R² is better for comparison: Every time you add a new predictor, R² can only go up — even if the variable is random noise. Adjusted R² corrects for this by penalizing model complexity (number of predictors). Here, adjusted R² = 0.918 ≈ 0.92, confirming that the SMB and HML factors genuinely improve the model and the gain isn’t just from adding more variables.


Q3. Logistic Regression for Market Direction

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

Today: \(r_{t-1} = 0.010\), \(\Delta VIX = 1.5\)

Part (j)

Calculate log-odds then apply sigmoid:

\[\text{logit} = -0.02 + 5.4(0.010) + (-0.38)(1.5)\]

\[P(\text{Up}) = \frac{1}{1 + e^{-\text{logit}}}\]

b0 <- -0.02
b1 <- 5.4
b2 <- -0.38

r_t1  <- 0.010
dvix  <- 1.5

log_odds <- b0 + b1*r_t1 + b2*dvix
prob     <- 1 / (1 + exp(-log_odds))
pred     <- ifelse(prob >= 0.5, "Up", "Down")

cat("Log-odds:", round(log_odds, 4), "\n")
## Log-odds: -0.536
cat("P(Up):   ", round(prob, 4), "\n")
## P(Up):    0.3691
cat("Class:   ", pred, "\n")
## Class:    Down

Log-odds = -0.536, giving P(Up) = 0.3691. At a 0.5 threshold, the predicted class is Down.

Part (k)

Sign of β₁ = +5.4 (lagged return): Positive. When yesterday’s return was positive, the model assigns higher probability to an “Up” day today. This captures return momentum — the tendency for recent gains to predict near-term gains. This is consistent with empirical momentum anomalies documented in the literature.

Sign of β₂ = -0.38 (ΔVIX): Negative. When the VIX increases (markets get more fearful/uncertain), the probability of an “Up” day tomorrow falls. This captures the risk-off effect — rising fear index predicts downward pressure on returns. Intuitively, a VIX spike often accompanies or precedes market drawdowns.

Part (l)

Confusion matrix:

Actual Up Actual Down
Predicted Up 67 44
Predicted Down 33 56
TP <- 67; FP <- 44
FN <- 33; TN <- 56
N  <- TP + FP + FN + TN

accuracy    <- (TP + TN) / N
sensitivity <- TP / (TP + FN)
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

\[\text{Accuracy} = \frac{67 + 56}{200} = 0.615\]

\[\text{Sensitivity} = \frac{67}{67+33} = 0.67\]

\[\text{Specificity} = \frac{56}{56+44} = 0.56\]

\[\text{Precision} = \frac{67}{67+44} = 0.6036\]

Part (m)

# Balanced classes: 100 Up, 100 Down
# Naive model predicts majority class always
# Since 50/50, naive accuracy = 50%
naive_acc <- 100 / 200
cat("Naive accuracy:", naive_acc, "\n")
## Naive accuracy: 0.5
cat("Model accuracy:", round(accuracy, 4), "\n")
## Model accuracy: 0.615
cat("Model beats naive?", accuracy > naive_acc, "\n")
## Model beats naive? TRUE

The dataset is balanced (100 Up, 100 Down), so a naive always-predict-Up classifier gets 50% accuracy. Our model achieves 0.615 > 0.50, so yes, the model beats the naive rule.

Why accuracy alone is inadequate for trading: In a real trading strategy, false positives (predicting “Up” when market goes “Down”) and false negatives (predicting “Down” when market goes “Up”) have very different financial consequences. Accuracy treats both errors symmetrically, which doesn’t reflect the actual profit/loss from trades. Moreover, in practice class frequencies aren’t 50/50, so a naive classifier can look impressive on accuracy while being useless.

A more economically relevant criterion would be the Sharpe ratio of the trading strategy derived from the model’s signals — this captures both the return and the risk of acting on predictions, and directly measures what investors care about.


Q4. Resampling and Regularization

Given: \(\bar{r} = 0.70\%\), \(\hat\sigma = 5.50\%\), \(T = 48\) months.

Part (n)

Monthly Sharpe ratio:

\[SR_m = \frac{\bar{r}}{\hat\sigma}\]

Annualized:

\[SR_{\text{annual}} = SR_m \times \sqrt{12}\]

r_mean <- 0.0070
r_sd   <- 0.0550

SR_m <- r_mean / r_sd
SR_a <- SR_m * sqrt(12)

cat("Monthly SR:    ", round(SR_m, 4), "\n")
## Monthly SR:     0.1273
cat("Scaling factor: sqrt(12) =", round(sqrt(12), 4), "\n")
## Scaling factor: sqrt(12) = 3.4641
cat("Annual SR:     ", round(SR_a, 4), "\n")
## Annual SR:      0.4409

\[SR_m = \frac{0.0070}{0.0550} = 0.1273\]

\[SR_{\text{annual}} = 0.1273 \times \sqrt{12} = 0.4409\]

The scaling factor is \(\sqrt{12}\) because if we scale monthly mean by 12 and monthly SD by \(\sqrt{12}\) to get annual estimates, the Sharpe ratio scales by \(12 / \sqrt{12} = \sqrt{12}\).

Part (o)

Bootstrap procedure for SE of Sharpe ratio (step by step):

  1. Start with the \(T = 48\) observed monthly returns \(\{r_1, r_2, \ldots, r_{48}\}\).
  2. Draw a bootstrap sample of size 48 by sampling with replacement from the original data.
  3. Compute the Sharpe ratio for this bootstrap sample: \(SR_b^* = \bar{r}_b^* / \hat\sigma_b^*\).
  4. Repeat steps 2–3 for \(B = 1000\) iterations (or more).
  5. The bootstrap SE is: \(\widehat{SE}_{boot}(SR) = \text{sd}(SR_1^*, SR_2^*, \ldots, SR_B^*)\).

Why i.i.d. bootstrap is inappropriate: Monthly returns are not i.i.d. They exhibit autocorrelation (momentum, mean-reversion), and volatility clustering (GARCH-type behavior). The plain i.i.d. bootstrap destroys the time-series dependence by shuffling observations randomly — the resulting bootstrap samples do not look like real monthly return sequences.

Fix: Use a moving block bootstrap. Instead of drawing individual returns, draw overlapping blocks of consecutive months (e.g., blocks of length 6 or 12). This preserves autocorrelation and volatility clustering within blocks. The block length should be chosen to cover the dependence horizon of the data.

Part (p)

cat("lambda_min = 0.030, factors retained: 14\n")
## lambda_min = 0.030, factors retained: 14
cat("lambda_1se = 0.065, factors retained:  7\n")
## lambda_1se = 0.065, factors retained:  7

I would deploy \(\lambda = 0.065\) (the one-standard-error rule, 7 factors).

Reasons:

  1. Parsimony: With 60 candidate factors and only a finite sample, the minimum-CV model (14 factors) risks overfitting — it has likely fit some noise. In finance, the signal-to-noise ratio is notoriously low, so fewer, more robust factors are preferred.

  2. The 1-SE rule logic: The 7-factor model’s cross-validation error is statistically indistinguishable from the best model (within one standard error). Since both models perform similarly out-of-sample, we should prefer the simpler one (Occam’s Razor).

  3. Out-of-sample generalization: Simpler models tend to generalize better to new data, especially in non-stationary environments like financial markets where factor relationships change over time.

Part (q)

Walk-forward (time-respecting) cross-validation scheme:

  1. Set an initial training window, e.g., first 24 months.
  2. Train the LASSO model on the training window to get factor weights.
  3. Generate a prediction for month 25 (the first out-of-sample month).
  4. Record the actual return and the strategy P&L.
  5. Extend the training window to include month 25, re-estimate the model.
  6. Predict month 26. Repeat until all months are evaluated.
  7. Aggregate OOS predictions to compute the Sharpe ratio, hit rate, etc.

Why random k-fold is unsafe for time-series data:

Random k-fold splits the data randomly, so the training fold can contain data from the future relative to the test fold. For example, training on months 30–48 and testing on month 15 is using future information to predict the past — look-ahead bias. In a real trading system, this is impossible: you can only use data available at the time of the trade. Random k-fold would make the strategy look artificially good, and the reported performance would not hold out-of-sample in live trading. The walk-forward scheme avoids this by always training on the past and testing on the immediate future, which mirrors the actual deployment environment.