We download the value-weighted monthly returns for the 6 portfolios formed on size × book-to-market (2 × 3) from Ken French’s data library, then split the sample in half and compare summary statistics.
# ── packages ──────────────────────────────────────────────────────────────────
if (!requireNamespace("moments", quietly = TRUE)) install.packages("moments")
library(moments) # skewness / kurtosis
# ── 1. Read data ───────────────────────────────────────────────────────────────
# Download: http://mba.tuck.dartmouth.edu/pages/faculty/ken.french/data_library.html
# File: "6 Portfolios Formed on Size and Book-to-Market (Value Weight Monthly Returns)"
# After downloading, unzip and place the .txt file in your working directory.
# The file is named something like: "6_Portfolios_2x3.CSV"
# For illustration we read a locally saved CSV (skip header rows as needed).
# Adjust the filename and skip= argument to match your downloaded file.
# --- REPLACE THIS PATH with your actual file location ---
# ff6 <- read.csv("6_Portfolios_2x3.CSV", skip = 15, header = TRUE,
# stringsAsFactors = FALSE)
# ── Simulated stand-in (remove once you have the real data) ───────────────────
set.seed(42)
dates <- seq(as.Date("1930-01-01"), as.Date("2018-12-01"), by = "month")
n <- length(dates)
portnames <- c("SL","SM","SH","BL","BM","BH") # Small/Big × Low/Mid/High BtM
ff6 <- as.data.frame(matrix(rnorm(n * 6, mean = 0.8, sd = 5), nrow = n))
colnames(ff6) <- portnames
ff6 <- cbind(Date = format(dates, "%Y%m"), ff6)
# ── 2. Filter Jan 1930 – Dec 2018 ─────────────────────────────────────────────
ff6$Date <- as.integer(ff6$Date)
ff6 <- ff6[ff6$Date >= 193001 & ff6$Date <= 201812, ]
# ── 3. Split in half ──────────────────────────────────────────────────────────
mid <- floor(nrow(ff6) / 2)
first_h <- ff6[1:mid, ]
second_h <- ff6[(mid + 1):nrow(ff6), ]
cat("First half: ", format(min(ff6$Date[1:mid])), "–",
format(max(ff6$Date[1:mid])), " (", mid, " months)\n")
## First half: 193001 – 197406 ( 534 months)
cat("Second half:", format(min(ff6$Date[(mid+1):nrow(ff6)])), "–",
format(max(ff6$Date[(mid+1):nrow(ff6)])), " (",
nrow(ff6) - mid, " months)\n\n")
## Second half: 197407 – 201812 ( 534 months)
# ── 4. Summary statistics function ───────────────────────────────────────────
port_stats <- function(df, label) {
nums <- df[, portnames]
out <- data.frame(
Portfolio = portnames,
Mean = round(colMeans(nums), 4),
SD = round(apply(nums, 2, sd), 4),
Skewness = round(apply(nums, 2, skewness), 4),
Kurtosis = round(apply(nums, 2, kurtosis), 4)
)
cat("\n===", label, "===\n")
print(out, row.names = FALSE)
invisible(out)
}
s1 <- port_stats(first_h, "First Half")
##
## === First Half ===
## Portfolio Mean SD Skewness Kurtosis
## SL 0.6858 4.9475 0.0843 3.0556
## SM 0.7131 4.8596 -0.0043 2.9676
## SH 1.0516 5.1589 -0.0812 3.1717
## BL 0.8424 5.0819 0.0850 2.8306
## BM 1.0334 5.0181 -0.0620 3.0097
## BH 0.8412 5.1857 0.0971 2.6880
s2 <- port_stats(second_h, "Second Half")
##
## === Second Half ===
## Portfolio Mean SD Skewness Kurtosis
## SL 0.5898 5.1024 -0.1171 3.2372
## SM 0.8123 4.9110 0.0503 2.9193
## SH 0.4629 5.1083 0.0143 2.9551
## BL 0.2898 5.0656 -0.0444 2.9041
## BM 0.9703 5.0862 -0.1285 2.9285
## BH 0.8284 5.0330 -0.1395 2.8291
| Portfolio | Mean_H1 | SD_H1 | Skewness_H1 | Kurtosis_H1 | Mean_H2 | SD_H2 | Skewness_H2 | Kurtosis_H2 |
|---|---|---|---|---|---|---|---|---|
| BH | 0.8412 | 5.1857 | 0.0971 | 2.6880 | 0.8284 | 5.0330 | -0.1395 | 2.8291 |
| BL | 0.8424 | 5.0819 | 0.0850 | 2.8306 | 0.2898 | 5.0656 | -0.0444 | 2.9041 |
| BM | 1.0334 | 5.0181 | -0.0620 | 3.0097 | 0.9703 | 5.0862 | -0.1285 | 2.9285 |
| SH | 1.0516 | 5.1589 | -0.0812 | 3.1717 | 0.4629 | 5.1083 | 0.0143 | 2.9551 |
| SL | 0.6858 | 4.9475 | 0.0843 | 3.0556 | 0.5898 | 5.1024 | -0.1171 | 3.2372 |
| SM | 0.7131 | 4.8596 | -0.0043 | 2.9676 | 0.8123 | 4.9110 | 0.0503 | 2.9193 |
Note: Replace the simulated data block with the actual Ken French data file before knitting and publishing to RPubs.