# Connect to PostgreSQL database
library(RPostgreSQL)
drv <- dbDriver("PostgreSQL")
pg <- dbConnect(drv, dbname = "crsp")
# Pull in votes from ISS Voting Analytics
rs <- dbGetQuery(pg, "DROP TABLE IF EXISTS destagger.votes")
rs <- dbGetQuery(pg, "
CREATE TABLE destagger.votes AS
SELECT * FROM issvoting.compvotes
WHERE itemdesc ILIKE '%declassify %' OR issagendaitemid IN ('M0215', 'S0201')
ORDER BY issagendaitemid")
# Add PERMNO and GVKEY to the table (match to CUSIP)
rs <- dbGetQuery(pg,
"ALTER TABLE destagger.votes ADD COLUMN gvkey character(6)")
rs <- dbGetQuery(pg,
"ALTER TABLE destagger.votes ADD COLUMN permno integer")
rs <- dbGetQuery(pg,
"UPDATE destagger.votes AS a SET gvkey =
(SELECT gvkey FROM destagger.gvkeys AS b
WHERE b.cusip=a.cusip)")
rs <- dbGetQuery(pg,
"UPDATE destagger.votes AS a SET permno =
(SELECT DISTINCT permno FROM crsp.stocknames AS b
WHERE b.ncusip=substr(a.cusip,1,8))")
# Pull data into R
declassify <- dbGetQuery(pg, "SELECT * FROM destagger.votes")
# Get returns around meeting dates
dropbox.path <- path.expand("~/Dropbox/research/AGL/")
source(paste(dropbox.path,"Code/R/getEventReturns.R",sep=""))
ret_data <- getEventReturns(declassify$permno, declassify$meetingdate,
days.before=0, days.after=1)
## Initializing database connection... 0.01 seconds
## Preparing data for upload to database... 0.00 seconds
## Writing list of PERMNOs and event dates to database...0.10 seconds
## Identifying relevant trading dates... 0.05 seconds
## Compounding raw returns... 0.61 seconds
## Compounding value-weighted market returns... 0.02 seconds
## Compounding size-decile market returns... 0.02 seconds
ret_data$row.names <- NULL
ret_data <- unique(ret_data) # Drop duplicates (multiple votes at a single meeting)
# Put return data back in the database
rs <- dbWriteTable(pg, c("destagger", "ret_data"), ret_data,
overwrite=TRUE, row.names=FALSE)
# Get data including a measure of shareholder support relative to "base"
# (e.g., shares outstanding) specified for each vote.
declassify <- dbGetQuery(pg, "SELECT *,
CASE WHEN base='Outstanding' AND outstandingshares>0 THEN votedfor/outstandingshares
WHEN base='F+A' AND votedfor+votedagainst>0
THEN votedfor/(votedfor+votedagainst)
WHEN base='F+A+AB' AND votedfor+votedagainst+votedabstain>0
THEN votedfor/(votedfor+votedagainst+votedabstain)
END AS percent_for
FROM destagger.votes AS a
LEFT JOIN destagger.ret_data AS b
USING (permno)
WHERE a.meetingdate=b.event_date AND result IN ('Fail', 'Pass')")
dbDisconnect(pg)
## [1] TRUE
Does ISS ever vary in its recommendation? Rarely so; it almost always recommends shareholders vote to declassify.
with(declassify, table(mgtrec, issrec))
## issrec
## mgtrec Against Do Not Vote For NULL
## Abstain 0 0 1 0
## Against 4 6 392 1
## For 0 4 333 0
## None 0 0 24 0
subset(declassify, issrec == "Against")
## permno companyname cusip ticker meetingdate
## 141 38682 R. R. Donnelley & Sons Co. 257867101 RRD 2007-05-24
## 220 86109 R.H. Donnelley Corp. 74955W307 RHD 2008-05-15
## 244 25419 Whirlpool Corporation 963320106 WHR 2009-04-21
## 592 28804 Alaska Air Group, Inc. 011659109 ALK 2006-05-16
## sponsor issagendaitemid itemonagendaid
## 141 Shareholder S0201 7033966
## 220 Shareholder S0201 7352214
## 244 Unkown S0201 7593518
## 592 Shareholder S0201 6779448
## itemdesc ballotitemnumber mgtrec issrec
## 141 Declassify the Board of Directors 8 Against Against
## 220 Declassify the Board of Directors 6 Against Against
## 244 Declassify the Board of Directors 7 Against Against
## 592 Declassify the Board of Directors 9 Against Against
## voterequirements base outstandingshares votedfor votedagainst
## 141 0.5 F+A+AB 219077233 45589073 135747179
## 220 0.5 F+A+AB 68787618 11752947 35121280
## 244 0.5 F+A+AB 73556811 32009789 26751394
## 592 0.5 F+A+AB 35965862 5135023 19681598
## votedabstain brokernonvotes result companyid meetingid
## 141 1829322 14384596 Fail NA NA
## 220 30269 0 Fail NA NA
## 244 302268 7036597 Pass 166363 493642
## 592 1003196 3711658 Fail NA NA
## agendageneraldesc mgmtrec voterequirement
## 141 NA
## 220 NA
## 244 Declassify the Board of Directors Against 0.5
## 592 NA
## outstandingshare brokernonvote voteresult usource countryofinc
## 141 NA NA
## 220 NA NA
## 244 73556811 7036597 Pass 2009-2010
## 592 NA NA
## votedfor1yr votedfor2yr votedfor3yr source gvkey event_date
## 141 NA NA NA 2007 004040 2007-05-24
## 220 NA NA NA 2008 111631 2008-05-15
## 244 NA NA NA 2009-2010 011465 2009-04-21
## 592 NA NA NA 2005_2006 001230 2006-05-16
## ret ret_sz ret_mkt percent_for
## 141 -0.005612 -0.001187 -0.0005972 0.2489
## 220 -0.022556 -0.035535 -0.0361717 0.2506
## 244 0.083476 0.071009 0.0665522 0.5420
## 592 -0.061137 -0.045813 -0.0430545 0.1989
There are 765 votes on declassification in the sample, with 18.43% being rejections by shareholders. Here they are by year.
## Descriptive statistics
declassify$year <- format(declassify$meetingdate, "%Y")
with(declassify, table(year, result))
## result
## year Fail Pass
## 2001 19 32
## 2002 18 33
## 2003 13 58
## 2004 8 70
## 2005 11 83
## 2006 10 99
## 2007 13 65
## 2008 20 108
## 2009 29 76
The votes are fairly well dispersed around the cutoff.
Note that these are only shareholder-sponsored votes.
# Produce a histogram of (SVT - allowable cap)
library(ggplot2)
qplot(percent_for - voterequirements,
data=subset(declassify,
percent_for <1 & percent_for > 0.01 & sponsor !="Management"),
binwidth=.01,
main="Histogram: Shareholder support minus vote requirement",
xlab="Shareholder support minus vote requirement")
But there's virtually no response by the market to the result of the vote.
qplot(percent_for - voterequirements, ret_mkt,
data=subset(declassify,
percent_for <1 & percent_for > 0.01 & sponsor !="Management"),
main="Market-adjusted Stock Returns: Days (0,1) Around Meeting Dates",
xlab="Shareholder support minus vote requirement",
color=issrec)
source("http://iangow.me/~iangow/rd_opt_bw.R")
### (4) Calculate the treatment effect and standard errors.
rd_data <- subset(declassify, subset = percent_for < 1 & percent_for >
0.01 & sponsor != "Management", select = c(ret_mkt, percent_for, voterequirements))
rd_data$x <- rd_data$percent_for - rd_data$voterequirements
rd_data$y <- rd_data$ret_mkt
# First, get bandwidth
h <- with(rd_data, rd_opt_bw(y, x, c = 0))
X <- rd_data$percent_for - rd_data$voterequirements
c <- 0
## Weights based on triangular kernel
wgts <- with(rd_data, (1 - abs(X - c)/h) * (abs(X - c) <= h))
local.lm <- lm(y ~ (x >= c) * x, weights = wgts, data = rd_data)
rd.est <- coef(local.lm)[[2]]
sd.est <- sqrt(vcov(local.lm)[2, 2])
## Output
out <- c(h, rd.est, sd.est, rd.est/sd.est)
names(out) <- c("Optimal Bandwidth", "RD Estimate", "Standard Error",
"t-statistic")
print(out)
## Optimal Bandwidth RD Estimate Standard Error t-statistic
## 0.187689 0.002056 0.007844 0.262074
# A function to estimate local linear regression around a point (x_i)
# using bandwidth h, the triangular kernel and limiting observations to
# those on the same side of the cutoff c. In some sense, this is purely
# visual, as the IK bandwidth is optimized for x \in [c-h, c+h] and other
# data are not involved.
ll <- function(x_i, y, x, h, c) {
wgts <- (1 - abs(x - x_i)/h) * (abs(x - x_i) <= h) * (sign(x_i - c) == sign(x -
c) | x_i == c)
lm.fitted <- lm(y ~ x, weights = wgts)
if (sum(wgts > 0) > 10) {
# Require 10 observations around x_i
return(predict(lm.fitted, newdata = data.frame(x = x_i)))
} else {
return(NA)
}
}
# Add the fitted value to the dataset
library(parallel)
rd_data$y_fitted <- with(rd_data, unlist(mclapply(X, ll, y, X, h,
c, mc.cores = 12)))
# Make a plot
library(ggplot2)
ggplot(rd_data, aes(x)) + geom_point(aes(y = y, color = x > c)) +
geom_line(aes(y = y_fitted, color = x > c))
While there is essentially no variation in how ISS recommends its clients vote on shareholder proposals to declassify boards, it seems there is quite a bit of variation in how mutual funds vote. Some funds mostly side with management, others go with ISS (though it is very difficult to argue causality without additional evidence), while others mix it up. It might be interesting to see whether the ones that mix it up do so in a way consistent with shareholder-value maximization, but inconsistent with the view of ISS (and Bebchuk) of a one-size-fits-all solution here. For example, if we looked at stock returns around the time of the vote, is there a relationship (say, using RDD) between the outcome of the vote, stock returns, and how these mutual funds voted. More specifically, if the “horses for courses” shareholders voted to reject the shareholder proposal, did the market react positively (negatively) to a failed (passed) proposal? (Note that it's not clear exactly how to measure how “horses for courses” shareholders voted, as it's unlikely that all such shareholders voted in the same way on each measure.)
library(ggplot2)
m <- qplot(votes_for/(votes_for + votes_against), votes_for + votes_against,
data = subset(fund.data, mgtrec %in% c("For", "Against")), color = mgtrec)
m + scale_y_log10()