--- title: "Exercise template for 'Ball and Brown (1968)'" author: Your name format: html bibliography: book.bib --- The supplied replication setup executes with its code hidden. The `wrds_data`, `size_rets`, and `me_breakpoints_raw` chunks cache only the local data frames needed later. Connections are created on each run; clear these caches to refresh the underlying data snapshot. ```{r} #| include: false #| message: false library(tidyverse) library(DBI) library(farr) library(dbplyr) # window_order() ``` ## Principal results of @Ball:1968ub ### Discussion questions 1. What is the research question of @Ball:1968ub? Do you find the paper to be persuasive? 2. What do you notice about the references in @Ball:1968ub [pp. 177-178]? 3. Given that "the most fundamental result" of @Ball:1968ub relates to an association or correlation, is it correct to say that the paper provides no evidence on causal linkages? Does this also mean that @Ball:1968ub is a "merely" descriptive paper according to the taxonomy of research papers outlined in [Chapter 4](https://iangow.github.io/far_book/causal-inf.html)? How might the results of @Ball:1968ub be represented in a causal diagram assuming that accounting information is meaningful and markets are efficient? Would an alternative causal diagram be assumed by a critic who viewed accounting information as meaningless? 4. Describe how Figure 1 of @Ball:1968ub supports each of principal results identified by @Ball:2019wu. 5. Consider the causal diagrams you created above. Do the results of @Ball:1968ub provide more support for one causal diagram than the other? 6. Compare Figure 1 of @Ball:2019wu with Figure 1 of BB68. What is common between the two figures? What is different? 7. What does "less their average" mean in the title of Figure 1 of @Ball:2019wu? What effect does this have on the plot? (Does it make this plot different from Figure 1 of BB68? Is information lost in the process?) 8. @Ball:2019wu [p. 418] say "in this replication we address two issues with the BB68 significance tests." Do you understand the points being made here? 9. @Ball:2019wu [p. 418] also say "the persistence of PEAD over time is evidence it does not constitute market inefficiency." What do you make of this argument? 10. What is the minimum amount of information that our hypothetical genie needs to provide to enable formation of the portfolios underlying *TI*, *NI*, and *II*? What are the rules for construction of each of these portfolios? 11. @Ball:1968ub observe a ratio of *NI* to *TI* of about 0.23. What do we expect this ratio to be? Does this ratio depend on the information content of accounting information? 12. Consider the paragraph in @Ball:2019wu [p. 418] beginning "an innovation in BB68 was to estimate ...". How do the discussions of these results differ between @Ball:1968ub and @Ball:2019wu? 13. Consider column (4) of Table 2 of @Ball:2019wu. Is an equivalent set of numbers reported in BB68? What is the underlying investment strategy associated with this column (this need not be feasible in practice)? 14. Heading 6.3 of @Ball:2019wu is "Does 'useful' disprove 'meaningless'?" Do you think that "not meaningless" implies "not useless"? Which questions (or facts) does BB68 address in these terms? ## Replicating @Ball:1968ub ```{r} #| include: false db <- dbConnect(RPostgres::Postgres(), bigint = "integer", check_interrupts = TRUE) msf <- tbl(db, Id(schema = "crsp", table = "msf")) msi <- tbl(db, Id(schema = "crsp", table = "msi")) ccmxpf_lnkhist <- tbl(db, Id(schema = "crsp", table = "ccmxpf_lnkhist")) stocknames <- tbl(db, Id(schema = "crsp", table = "stocknames")) funda <- tbl(db, Id(schema = "comp", table = "funda")) fundq <- tbl(db, Id(schema = "comp", table = "fundq")) ``` ```{r} #| include: false #| label: wrds_data #| cache: true #| cache.vars: [rets_all, full_panel, rets, me_values, news] annc_events <- fundq |> filter(indfmt == "INDL", datafmt == "STD", consol == "C", popsrc == "D") |> filter(fqtr == 4, fyr == 12, !is.na(rdq)) |> select(gvkey, datadate, rdq) |> mutate(annc_month = as.Date(floor_date(rdq, unit = "month"))) crsp_dates <- msi |> select(date) |> window_order(date) |> mutate(td = row_number()) |> mutate(month = as.Date(floor_date(date, unit = "month"))) crsp_dates |> collect(n = 10) annc_months <- crsp_dates |> select(month, td) |> rename(annc_month = month, annc_td = td) |> mutate(start_td = annc_td - 11L, end_td = annc_td + 6L) annc_months |> collect(n = 10) td_link <- crsp_dates |> inner_join(annc_months, join_by(between(td, start_td, end_td))) |> mutate(rel_td = td - annc_td) |> select(annc_month, rel_td, date) td_link |> filter(annc_month == "2001-04-01") |> collect() |> print(n = Inf) ccm_link <- ccmxpf_lnkhist |> filter(linktype %in% c("LC", "LU", "LS"), linkprim %in% c("C", "P")) |> rename(permno = lpermno) |> mutate(linkenddt = coalesce(linkenddt, max(linkenddt, na.rm = TRUE))) |> select(gvkey, permno, linkdt, linkenddt) annc_returns <- annc_events |> filter(between(year(datadate), 1987L, 2002L)) |> inner_join(td_link, by = "annc_month") |> compute() rets_all <- annc_returns |> inner_join(ccm_link, by = "gvkey") |> filter(annc_month >= linkdt, annc_month <= linkenddt) |> inner_join(msf, by = c("permno", "date")) |> select(gvkey, datadate, rel_td, permno, date, ret) |> collect() |> inner_join( stocknames |> filter(exchcd %in% c(1, 2, 3)) |> select(permno, namedt, nameenddt) |> collect(), join_by(permno, between(date, namedt, nameenddt))) |> select(gvkey, datadate, rel_td, permno, date, ret) full_panel <- rets_all |> filter(!is.na(ret)) |> group_by(gvkey, datadate) |> mutate(n_obs = n()) |> ungroup() |> filter(n_obs == max(n_obs)) |> select(gvkey, datadate) rets <- rets_all |> semi_join(full_panel, by = c("gvkey", "datadate")) me_values <- msf |> mutate(mktcap = abs(prc) * shrout / 1000) |> select(permno, date, mktcap) |> mutate(month = as.Date(floor_date(date, unit = "month"))) |> filter(month(month) == 12) |> collect() news <- funda |> filter(indfmt == "INDL", datafmt == "STD", consol == "C", popsrc == "D") |> filter(fyr == 12) |> group_by(gvkey) |> window_order(datadate) |> mutate(lag_ibc = lag(ibc), lag_oancf = lag(oancf), lag_at = lag(at), lag_fyear = lag(fyear)) |> ungroup() |> filter(between(fyear, 1987, 2002), lag_fyear + 1 == fyear) |> mutate(earn_chg = if_else(lag_at > 0, (ibc - lag_ibc) / lag_at, NA), cfo_chg = if_else(lag_at > 0, (oancf - lag_oancf) / lag_at, NA), earn_gn = earn_chg > 0, cfo_gn = cfo_chg > 0) |> filter(!is.na(cfo_gn), !is.na(earn_gn)) |> select(gvkey, datadate, earn_chg, cfo_chg, earn_gn, cfo_gn) |> group_by(datadate) |> mutate(earn_decile = ntile(earn_chg, 10), cfo_decile = ntile(cfo_chg, 10)) |> ungroup() |> collect() ``` ```{r} #| include: false #| label: size_rets #| cache: true #| cache.vars: size_rets size_rets <- get_size_rets_monthly() ``` ```{r} #| include: false #| label: me_breakpoints_raw #| cache: true #| cache.vars: me_breakpoints_raw me_breakpoints_raw <- get_me_breakpoints(keep_max = TRUE) |> transmute(month, decile, cutoff = me_max) ``` ```{r} #| include: false me_breakpoints <- me_breakpoints_raw |> group_by(month) |> arrange(decile) |> mutate(me_min = coalesce(lag(cutoff), 0), me_max = cutoff) |> mutate(me_max = if_else(decile == 10, Inf, me_max)) |> select(-cutoff) |> ungroup() |> arrange(month, decile) me_decile_assignments <- me_values |> inner_join(me_breakpoints, join_by(month, mktcap >= me_min, mktcap < me_max)) |> mutate(year = as.integer(year(date)) + 1L) |> select(permno, year, decile) merged <- news |> mutate(year = year(datadate)) |> inner_join(rets, by = c("gvkey", "datadate")) |> inner_join(me_decile_assignments, by = c("permno", "year")) |> mutate(month = floor_date(date, unit = "month")) |> inner_join(size_rets, by = c("decile", "month")) |> select(-permno, -month) ``` ```{r} #| include: false bh_returns <- merged |> filter(!is.na(ret)) |> group_by(gvkey, datadate) |> arrange(rel_td, .by_group = TRUE) |> mutate(across(ends_with("ret"), \(x) cumprod(1 + x))) |> ungroup() plot_data <- bh_returns |> group_by(rel_td, earn_gn) |> summarize(across(ends_with("ret"), \(x) mean(x, na.rm = TRUE)), .groups = "drop") |> mutate(aret_ew = ret - ew_ret, aret_vw = ret - vw_ret) ``` ```{r} #| echo: false #| label: fig-bb-1 #| fig-height: 3 #| fig-cap: Replication of Figure 1 of @Ball:1968ub plot_data |> mutate(`Earnings news` = if_else(earn_gn, "Good", "Bad")) |> ggplot(aes(x = rel_td, y = aret_ew, linetype = `Earnings news`)) + geom_line() + theme(legend.position = "bottom") ``` ```{r} #| include: false max_cutoff <- me_breakpoints_raw |> filter(month == "2020-12-01", decile == 10) |> select(cutoff) |> pull() ``` ### Exercises 1. From the data below, we see that the upper bound for the tenth decile is about `r str_c("US$", prettyNum(max_cutoff/1000, digits = 3))` billion. How can we reconcile this with the existence of firms with market capitalizations over US$1 trillion? *Bonus*: Using data from `crsp.msf`, identify the firm whose market capitalization was `r str_c("US$", prettyNum(max_cutoff/1000, digits = 3))` billion in December 2020? (*Hint*: For the bonus question, you can add a `filter()` to code in the template to obtain the answer. Why do we need to group by `permco`, not `permno`, to find the answer?) ```{r} me_breakpoints_raw |> filter(month == "2020-12-01") ``` The following starting query is not run; add a filter for the bonus. ```{r} #| eval: false msf |> filter(date == "2020-12-31") |> mutate(mktcap = abs(prc) * shrout / 1000) |> group_by(permco, date) |> mutate(totmktcap = sum(mktcap, na.rm = TRUE)) |> ungroup() |> select(permno, permco, date, mktcap, totmktcap) |> arrange(totmktcap) ``` 2. To keep things straightforward, we focused on firms that have returns for each month in the $(t - 11, t+ 6)$ window. Can you tell what approach @Nichols:2004tb took with regard to this issue? 3. Table 2 of @Nichols:2004tb measures cumulative abnormal returns as the "cumulative raw return minus cumulative size decile portfolio to which the firm begins." Apart from the use of a size-decile portfolio rather than some other market index, how does this measure differ from the Abnormal Performance Index (API) defined on p.168 of @Ball:1968ub? Adjust the measure depicted in the replication of Figure 1 to more closely reflect the API definition used in @Ball:1968ub (but retaining the size-decile as the benchmark). Does this tweak significantly affect the results? Which approach seems most appropriate? That of @Nichols:2004tb or that of @Ball:1968ub? 4. Create an alternative version of @fig-bb-1 using the sign of "news" about cash flows in the place of income news. Do your results broadly line up with those in Panel A of Figure 2 of @Nichols:2004tb? Do these results imply that accounting income is inherently more informative than cash flows from operations? Why or why not? 5. Create an alternative version of the figure above focused on the extreme earnings deciles in place of the good-bad news dichotomy. Do your results broadly line up with those in Panel B of Figure 2 of @Nichols:2004tb? 6. Calculate *AI* by year following the formula on p. 175 of @Ball:1968ub (there denoted as $II_0$). You may find it helpful to start with the code producing `plot_data` above. You may also find it helpful to use `pivot_wider()` to get information about portfolios in each year into a single row. Note that you will only be interested in rows at $t = 0$ (e.g., `filter(rel_td == 0)`). 7. Calculate *NI* by year following the formula on p. 175 of @Ball:1968ub (there denoted as $NI_0$). Note that you will only be interested in rows at $t = 0$ (e.g., `filter(rel_td == 0)`). 8. Using the data on *NI* and *AI* from above, create a plot of $AI/NI$ like that in Figure 2 of @Ball:2019wu. Do you observe similar results to those shown in Figure 2 of @Ball:2019wu? ```{r} #| include: false dbDisconnect(db) ``` ```{r} #| include: false dbDisconnect(db) ```