- Create the Toy Dataset
letters_data <- tibble(
letter_id = 1:10,
manager_id = c(rep("Ackman", 5), rep("Loeb", 5)),
quarter = as.Date(c("2020-03-31", "2020-06-30", "2020-09-30", "2020-12-31", "2021-03-31",
"2020-03-31", "2020-06-30", "2020-09-30", "2020-12-31", "2021-03-31")),
letter_text = c(
"Ackman Letter Q1 2020 example.",
"Ackman Letter Q2 2020 example.",
"Ackman Letter Q3 2020 example.",
"Ackman Letter Q4 2020 example.",
"Ackman Letter Q1 2021 example.",
"Loeb Letter Q1 2020 example.",
"Loeb Letter Q2 2020 example.",
"Loeb Letter Q3 2020 example.",
"Loeb Letter Q4 2020 example.",
"Loeb Letter Q1 2021 example."
),
n_positive = c(14, 18, 20, 16, 12, 10, 11, 14, 13, 15),
n_negative = c(10, 12, 15, 9, 10, 8, 12, 13, 11, 10),
n_total_words = c(400, 420, 410, 430, 390, 395, 415, 400, 405, 420),
sentiment_score = (n_positive - n_negative) / n_total_words,
manager_return = c(0.03, 0.02, 0.01, 0.05, 0.04, 0.01, 0.03, 0.04, 0.06, 0.07),
sp500_return = c(-0.02, 0.08, 0.06, 0.09, 0.06, -0.02, 0.08, 0.06, 0.09, 0.06)
) %>%
mutate(alpha = manager_return - sp500_return)
head(letters_data)
## # A tibble: 6 × 11
## letter_id manager_id quarter letter_text n_positive n_negative
## <int> <chr> <date> <chr> <dbl> <dbl>
## 1 1 Ackman 2020-03-31 Ackman Letter Q1 2020 e… 14 10
## 2 2 Ackman 2020-06-30 Ackman Letter Q2 2020 e… 18 12
## 3 3 Ackman 2020-09-30 Ackman Letter Q3 2020 e… 20 15
## 4 4 Ackman 2020-12-31 Ackman Letter Q4 2020 e… 16 9
## 5 5 Ackman 2021-03-31 Ackman Letter Q1 2021 e… 12 10
## 6 6 Loeb 2020-03-31 Loeb Letter Q1 2020 exa… 10 8
## # ℹ 5 more variables: n_total_words <dbl>, sentiment_score <dbl>,
## # manager_return <dbl>, sp500_return <dbl>, alpha <dbl>
summary(letters_data)
## letter_id manager_id quarter letter_text
## Min. : 1.00 Length:10 Min. :2020-03-31 Length:10
## 1st Qu.: 3.25 Class :character 1st Qu.:2020-06-30 Class :character
## Median : 5.50 Mode :character Median :2020-09-30 Mode :character
## Mean : 5.50 Mean :2020-09-29
## 3rd Qu.: 7.75 3rd Qu.:2020-12-31
## Max. :10.00 Max. :2021-03-31
## n_positive n_negative n_total_words sentiment_score
## Min. :10.00 Min. : 8.0 Min. :390.0 Min. :-0.002410
## 1st Qu.:12.25 1st Qu.:10.0 1st Qu.:400.0 1st Qu.: 0.004970
## Median :14.00 Median :10.5 Median :407.5 Median : 0.007564
## Mean :14.30 Mean :11.0 Mean :408.5 Mean : 0.007988
## 3rd Qu.:15.75 3rd Qu.:12.0 3rd Qu.:418.8 3rd Qu.: 0.012123
## Max. :20.00 Max. :15.0 Max. :430.0 Max. : 0.016279
## manager_return sp500_return alpha
## Min. :0.0100 Min. :-0.020 Min. :-0.0600
## 1st Qu.:0.0225 1st Qu.: 0.060 1st Qu.:-0.0475
## Median :0.0350 Median : 0.060 Median :-0.0250
## Mean :0.0360 Mean : 0.054 Mean :-0.0180
## 3rd Qu.:0.0475 3rd Qu.: 0.080 3rd Qu.: 0.0025
## Max. :0.0700 Max. : 0.090 Max. : 0.0500
- Sentiment Score Over Time
ggplot(letters_data, aes(x = quarter, y = sentiment_score, color = manager_id)) +
geom_line() +
geom_point() +
geom_smooth(se = FALSE, method = "loess") +
facet_wrap(~ manager_id) +
theme_minimal() +
labs(
title = "Sentiment Score Over Time by Manager",
x = "Quarter",
y = "Sentiment Score"
)
## `geom_smooth()` using formula = 'y ~ x'
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : span too small. fewer data values than degrees of freedom.
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : pseudoinverse used at 18350
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : neighborhood radius 184.83
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : reciprocal condition number 0
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : There are other near singularities as well. 33792
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : span too small. fewer data values than degrees of freedom.
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : pseudoinverse used at 18350
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : neighborhood radius 184.83
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : reciprocal condition number 0
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : There are other near singularities as well. 33792

- Alpha Over Time (Manager Return minus S&P 500 Return)
ggplot(letters_data, aes(x = quarter, y = alpha, color = manager_id)) +
geom_line() +
geom_point() +
geom_smooth(se = FALSE, method = "loess") +
facet_wrap(~ manager_id) +
theme_minimal() +
labs(
title = "Alpha Over Time by Manager",
x = "Quarter",
y = "Alpha (Manager Return - S&P500)"
)
## `geom_smooth()` using formula = 'y ~ x'
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : span too small. fewer data values than degrees of freedom.
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : pseudoinverse used at 18350
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : neighborhood radius 184.83
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : reciprocal condition number 0
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : There are other near singularities as well. 33792
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : span too small. fewer data values than degrees of freedom.
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : pseudoinverse used at 18350
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : neighborhood radius 184.83
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : reciprocal condition number 0
## Warning in simpleLoess(y, x, w, span, degree = degree, parametric = parametric,
## : There are other near singularities as well. 33792

- Panel Regressions (Fixed Effects)
# Regression 1: Alpha ~ Sentiment Score
model_alpha <- feols(alpha ~ sentiment_score | manager_id + quarter, data = letters_data, cluster = ~manager_id)
# Regression 2: Manager Return ~ Sentiment Score
model_manager_return <- feols(manager_return ~ sentiment_score | manager_id + quarter, data = letters_data, cluster = ~manager_id)
# Regression 3: S&P500 Return ~ Sentiment Score
model_sp500_return <- feols(sp500_return ~ sentiment_score | manager_id + quarter, data = letters_data, cluster = ~manager_id)
knitr::kable(head(letters_data), caption = "First few rows of letters_data")
First few rows of letters_data
| 1 |
Ackman |
2020-03-31 |
Ackman Letter Q1 2020 example. |
14 |
10 |
400 |
0.0100000 |
0.03 |
-0.02 |
0.05 |
| 2 |
Ackman |
2020-06-30 |
Ackman Letter Q2 2020 example. |
18 |
12 |
420 |
0.0142857 |
0.02 |
0.08 |
-0.06 |
| 3 |
Ackman |
2020-09-30 |
Ackman Letter Q3 2020 example. |
20 |
15 |
410 |
0.0121951 |
0.01 |
0.06 |
-0.05 |
| 4 |
Ackman |
2020-12-31 |
Ackman Letter Q4 2020 example. |
16 |
9 |
430 |
0.0162791 |
0.05 |
0.09 |
-0.04 |
| 5 |
Ackman |
2021-03-31 |
Ackman Letter Q1 2021 example. |
12 |
10 |
390 |
0.0051282 |
0.04 |
0.06 |
-0.02 |
| 6 |
Loeb |
2020-03-31 |
Loeb Letter Q1 2020 example. |
10 |
8 |
395 |
0.0050633 |
0.01 |
-0.02 |
0.03 |
- Combined Regression Table
modelsummary(
list(
"Alpha" = model_alpha,
"Manager Return" = model_manager_return,
"S&P 500 Return" = model_sp500_return
),
stars = TRUE,
gof_omit = "IC|Log|Adj|F|RMSE",
title = "Panel Regressions: Sentiment Predicting Returns",
notes = "Standard errors clustered at manager level; Manager and Quarter fixed effects included."
)
Panel Regressions: Sentiment Predicting Returns
| |
Alpha |
Manager Return |
S&P 500 Return |
| + p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001 |
| Standard errors clustered at manager level; Manager and Quarter fixed effects included. |
| sentiment_score |
0.514*** |
0.514*** |
0.000 |
|
(0.000) |
(0.000) |
(0.000) |
| Num.Obs. |
10 |
10 |
10 |
| R2 |
0.934 |
0.781 |
1.000 |
| R2 Within |
0.049 |
0.049 |
0.000 |
| Std.Errors |
by: manager_id |
by: manager_id |
by: manager_id |