# Connect to PostgreSQL database
library(RPostgreSQL)
drv <- dbDriver("PostgreSQL")
pg <- dbConnect(drv, dbname = "crsp")
rs <- dbGetQuery(pg, "DROP TABLE IF EXISTS destagger.mv_fund_rank")
rs <- dbGetQuery(pg, "
CREATE TABLE destagger.mv_fund_rank AS
SELECT instid, num_votes, rank() OVER (ORDER BY num_votes DESC) AS fund_rank
FROM (
SELECT instid, count(*) AS num_votes
FROM (
SELECT DISTINCT instid, itemonagendaid
FROM issvoting.fund_votes
WHERE issagendaitemid='S0212') AS b
GROUP BY instid
ORDER BY num_votes DESC) AS a")
# Pull in votes from ISS Voting Analytics
rs <- dbGetQuery(pg, "DROP TABLE IF EXISTS destagger.mv_votes")
sql <- "
CREATE TABLE destagger.mv_votes AS
SELECT * FROM issvoting.compvotes AS a
LEFT JOIN (
SELECT itemonagendaid, min(fund_rank) AS rank_fund_against
FROM issvoting.fund_votes AS a
INNER JOIN destagger.mv_fund_rank AS b
USING (instid)
WHERE fundvote='Against'
GROUP BY itemonagendaid) AS b
USING (itemonagendaid)
WHERE issagendaitemid='S0212'
ORDER BY issagendaitemid"
rs <- dbGetQuery(pg, sql)
# Add PERMNO and GVKEY to the table (match to CUSIP)
rs <- dbGetQuery(pg,
"ALTER TABLE destagger.mv_votes ADD COLUMN gvkey character(6)")
rs <- dbGetQuery(pg,
"ALTER TABLE destagger.mv_votes ADD COLUMN permno integer")
rs <- dbGetQuery(pg,
"UPDATE destagger.mv_votes AS a SET gvkey =
(SELECT gvkey FROM destagger.gvkeys AS b
WHERE b.cusip=a.cusip)")
rs <- dbGetQuery(pg,
"UPDATE destagger.mv_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.mv_votes")
rs <- dbDisconnect(pg)
library(RPostgreSQL)
drv <- dbDriver("PostgreSQL")
pg <- dbConnect(drv, dbname = "crsp")
# 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=0)
## Initializing database connection... 0.00 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.03 seconds
## Compounding raw returns... 0.19 seconds
## Compounding value-weighted market returns... 0.01 seconds
## Compounding size-decile market returns... 0.01 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", "mv_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.mv_votes AS a
LEFT JOIN destagger.mv_ret_data AS b
USING (permno)
WHERE a.meetingdate=b.event_date AND result IN ('Fail', 'Pass')")
rs <- dbDisconnect(pg)
Does ISS ever vary in its recommendation? Rarely so; it almost always recommends shareholders vote to adopt majority voting.
with(declassify, table(mgtrec, issrec))
## issrec
## mgtrec Against Do Not Vote For
## Abstain 0 0 1
## Against 5 1 239
## For 0 1 7
## None 0 0 4
There are 258 votes on majority voting in the sample, with 58.53% 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
## 2005 42 13
## 2006 57 37
## 2007 22 15
## 2008 13 12
## 2009 17 30
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),
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),
main="Market-adjusted Stock Returns: Days (0,1) Around Meeting Dates",
xlab="Shareholder support minus vote requirement",
color=issrec)
qplot(percent_for - voterequirements, ret_mkt,
data=subset(declassify,
percent_for <1 & percent_for > 0.01),
main="Market-adjusted Stock Returns: Days (0,1) Around Meeting Dates",
xlab="Shareholder support minus vote requirement",
color=mgtrec)
source("~/Dropbox/research/rdd/rd_opt_bw.R")
# source('http://iangow.me/~iangow/rd_opt_bw.R') (4) Calculate the
# treatment effect and standard errors.
table(declassify$rank_fund_against)
##
## 1 2 4 5 6 8 12 18 26 39 135 143
## 150 1 7 1 31 11 1 2 1 8 37 4
rd_data <- subset(declassify, subset = percent_for < 1 & percent_for >
0.01, select = c(ret_mkt, percent_for, voterequirements))
rd_data$fundvote <- 1
rd_data$x <- (rd_data$percent_for - rd_data$voterequirements) * rd_data$fundvote
rd_data$y <- rd_data$ret_mkt
# First, get bandwidth
h <- with(rd_data, rd_opt_bw(y, x, c = 0))
X <- rd_data$x
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.177364 0.006574 0.005441 1.208205
# 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 adopt majority voting, 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.)
## institutionname percent_for num_votes
## 1 Fidelity Management & Research 0.46774 376
## 2 BlackRock Advisors, Inc. 0.69970 344
## 3 Transamerica Funds 0.71472 341
## 4 IQ Investment Advisors LLC 0.72483 338
## 5 AST Investment Services, Inc. 0.66771 334
## 6 Prudential Financial 0.77083 307
## 7 John Hancock Funds, LLC 0.63704 297
## 8 Northern Trust Global Investments 0.50355 286
## 9 Jackson National Asset Management, LLC 0.89668 277
## 10 T. Rowe Price Associates, Inc. (MD) 0.98148 272
## 11 Phoenix Funds 0.87938 266
## 12 General Electric Asset Management 0.15000 263
## 13 Northwestern Mutual Funds 0.89370 258
## 14 RS Investment Management Co. LLC 0.89370 256
## 15 New York Life Investment Management LLC 0.86290 251
## 16 Vanguard Group, Inc. 0.08130 248
## 17 Russell Investment Group 0.82231 246
## 18 Munder Capital Management 0.96721 244
## 19 TIAA-CREF Asset Management LLC 0.79476 241
## 20 JPMorgan Asset Management, Inc. (US) 0.90789 240
## 21 RiverSource Investments LLC 0.97854 239
## 22 Rodney Square Management Corporation 0.06481 239
## 23 AIM Management Group, Inc. 0.93590 239
## 24 Rydex Investments 0.03211 236
## 25 Wells Fargo Funds Management, LLC 0.95575 226
## 26 Charles Schwab Investment Management, Inc. 0.94203 210
## 27 American Century Investment Management, Inc. 0.01951 209
## 28 Evergreen Investment Management Co., Inc. 0.97790 187
## 29 Nationwide Fund<a0>Advisors 0.63536 182
## 30 Nationwide Fund Advisors 0.63636 114
## 31 Charles Schwab Investment Management, In 0.97778 47
## 32 Evergreen Investment Management Co., Inc 0.97778 45
## 33 American Century Investment Management, 0.00000 41
## 34 Nationwide Fund Advisors 0.93333 32