Replication Lab: CEO Turnover and Firm Performance
Replicate key findings from the CEO turnover literature using Cox proportional hazards models. Simulate a CEO-firm panel matching stylized facts about CEO tenure, estimate hazard models with firm performance covariates, test the proportional hazards assumption, and distinguish voluntary from forced departures using competing risks.
Overview
In this replication lab, you will reproduce key empirical patterns from the literature on CEO turnover and firm performance:
Jenter, Dirk, and Fadi Kanaan. 2015. "CEO Turnover and Relative Performance Evaluation." Journal of Finance 70(5): 2155--2184.
The CEO turnover literature has consistently found that poor firm performance increases the hazard of CEO departure. A central question is whether boards evaluate CEOs based on absolute performance or relative performance (filtering out industry- or market-wide shocks). The headline finding: boards fire CEOs more often after poor stock returns, but they also fire CEOs after poor market-wide performance that is beyond the CEO's control, suggesting imperfect relative performance evaluation.
Why this paper matters: It provides evidence on the efficiency of corporate governance mechanisms and speaks to the broader question of how organizations evaluate and replace their leaders.
What you will do:
- Learn why simulation is used when proprietary executive data (ExecuComp, BoardEx) are unavailable
- Simulate a CEO-firm panel matching stylized facts about tenure, turnover rates, and performance
- Estimate Kaplan-Meier survival curves by performance quartile
- Fit Cox proportional hazards models with firm and industry performance
- Test the proportional hazards assumption using Schoenfeld residuals
- Estimate a competing risks model distinguishing voluntary from forced departures
Step 1: Simulate the CEO-Firm Panel
We simulate a panel of 1,500 CEO spells across 500 firms observed over 15 years. Each CEO spell has a tenure duration, a departure indicator (vs. right-censoring), and time-varying firm performance.
library(survival)
library(survminer)
set.seed(2015)
n_ceos <- 1500
# Firm characteristics
firm_id <- sample(1:500, n_ceos, replace = TRUE)
industry <- sample(c("tech","finance","manufacturing","healthcare","retail"),
n_ceos, replace = TRUE,
prob = c(0.25, 0.20, 0.25, 0.15, 0.15))
firm_size <- exp(rnorm(n_ceos, 8.5, 1.2)) # Log-normal assets
# CEO characteristics
ceo_age_start <- round(rnorm(n_ceos, 52, 7))
founder <- rbinom(n_ceos, 1, 0.10)
outsider <- rbinom(n_ceos, 1, 0.30)
# Firm performance (annualized stock return)
firm_return <- rnorm(n_ceos, 0.08, 0.25)
industry_return <- rnorm(n_ceos, 0.08, 0.12)
relative_return <- firm_return - industry_return
roa <- rnorm(n_ceos, 0.06, 0.08)
# Performance quartile
perf_quartile <- cut(firm_return,
breaks = quantile(firm_return, c(0, 0.25, 0.5, 0.75, 1)),
labels = c("Q1 (worst)","Q2","Q3","Q4 (best)"),
include.lowest = TRUE)
# Generate tenure using Weibull with performance-dependent hazard
# Higher hazard for poor performance, lower for founders
shape <- 1.2 # Slightly increasing hazard
scale_param <- exp(2.0 + 0.4 * firm_return + 0.3 * founder -
0.2 * outsider + 0.1 * log(firm_size / 1000))
tenure_latent <- rweibull(n_ceos, shape = shape, scale = scale_param)
# Right-censoring at observation window end
censor_time <- runif(n_ceos, 1, 15)
tenure <- pmin(tenure_latent, censor_time)
departed <- as.integer(tenure_latent <= censor_time)
# Departure type (conditional on departing)
# Forced departure more likely with poor performance
p_forced <- plogis(-1.0 - 2.0 * firm_return + 0.5 * outsider)
departure_type <- ifelse(departed == 0, "censored",
ifelse(runif(n_ceos) < p_forced, "forced", "voluntary"))
df <- data.frame(ceo_id = 1:n_ceos, firm_id, industry, firm_size,
ceo_age_start, founder, outsider,
firm_return, industry_return, relative_return, roa,
perf_quartile, tenure = round(tenure, 2),
departed, departure_type)
cat("=== CEO Spell Summary ===\n")
cat("N spells:", nrow(df), "\n")
cat("Departed:", sum(df$departed), " Censored:", sum(1 - df$departed), "\n")
cat("Median tenure:", round(median(df$tenure), 1), "years\n")
cat("Annual turnover rate:", round(mean(df$departed) / mean(df$tenure), 3), "\n")
cat("\n=== Departure Type ===\n")
print(table(df$departure_type))Step 2: Kaplan-Meier Survival Curves by Performance Quartile
Before fitting parametric or semi-parametric models, we examine how CEO survival varies by firm performance using the Kaplan-Meier estimator.
# Kaplan-Meier by performance quartile
km_fit <- survfit(Surv(tenure, departed) ~ perf_quartile, data = df)
# Print median survival by quartile
cat("=== Median Tenure by Performance Quartile ===\n")
print(km_fit)
# Log-rank test: do survival curves differ across quartiles?
lr_test <- survdiff(Surv(tenure, departed) ~ perf_quartile, data = df)
cat("\nLog-rank test chi-squared:", round(lr_test$chisq, 2),
" p-value:", format.pval(1 - pchisq(lr_test$chisq, 3)), "\n")
# Plot (optional — output is visual)
# ggsurvplot(km_fit, data = df, pval = TRUE,
# xlab = "Years", ylab = "Survival probability",
# title = "CEO Survival by Firm Performance Quartile")Why do we use Kaplan-Meier curves rather than simply comparing mean tenure across performance groups?
Step 3: Cox Proportional Hazards Model
We now estimate Cox PH models to quantify how firm performance, CEO characteristics, and governance variables affect the hazard of CEO departure.
# Model 1: Firm return only
cox1 <- coxph(Surv(tenure, departed) ~ firm_return, data = df)
# Model 2: Add CEO characteristics
cox2 <- coxph(Surv(tenure, departed) ~ firm_return + founder +
outsider + ceo_age_start, data = df)
# Model 3: Relative performance evaluation
cox3 <- coxph(Surv(tenure, departed) ~ firm_return + industry_return +
founder + outsider + ceo_age_start, data = df)
# Model 4: Full model with controls
cox4 <- coxph(Surv(tenure, departed) ~ firm_return + industry_return +
roa + founder + outsider + ceo_age_start +
log(firm_size), data = df)
cat("=== Model 1: Firm Return Only ===\n")
print(summary(cox1)$coefficients)
cat("\n=== Model 3: Relative Performance ===\n")
print(summary(cox3)$coefficients)
# Hazard ratios
cat("\n=== Hazard Ratios (Model 4) ===\n")
hr <- exp(coef(cox4))
print(round(hr, 3))
cat("\nKey: HR < 1 means lower hazard of departure (longer tenure)")
cat("\n HR > 1 means higher hazard of departure (shorter tenure)\n")Step 4: Test the Proportional Hazards Assumption
The Cox model assumes that hazard ratios are constant over time. If the effect of performance on turnover changes as tenure increases (e.g., a "honeymoon period" for new CEOs), this assumption is violated.
# Schoenfeld residual test
ph_test <- cox.zph(cox4)
print(ph_test)
cat("\n=== Interpretation ===\n")
cat("H0: PH assumption holds (coefficient is constant over time)\n")
cat("If p < 0.05, the PH assumption is violated for that covariate.\n")
# If PH violated, consider time-varying coefficients
if (any(ph_test$table[, "p"] < 0.05)) {
cat("\nPH violation detected. Consider:\n")
cat("1. Stratification on the violating variable\n")
cat("2. Time-varying coefficients (tt() function)\n")
cat("3. Accelerated failure time model instead\n")
# Stratified Cox: stratify on the most problematic variable
cox_strat <- coxph(Surv(tenure, departed) ~ firm_return +
industry_return + roa + outsider +
ceo_age_start + log(firm_size) +
strata(founder), data = df)
cat("\n=== Stratified Cox (strata = founder) ===\n")
print(summary(cox_strat)$coefficients)
}A researcher finds that the Schoenfeld test rejects the proportional hazards assumption for firm_return (p = 0.003). What does this imply?
Step 5: Competing Risks — Voluntary vs. Forced Departure
Not all CEO departures are alike. Forced departures (firings) are more likely driven by poor performance, while voluntary departures (retirements, moves to other firms) may be less performance-sensitive. A competing risks framework allows us to model these separately.
# Create competing risks indicators
df$event_forced <- as.integer(df$departure_type == "forced")
df$event_voluntary <- as.integer(df$departure_type == "voluntary")
# Cause-specific Cox models
# Forced departure (treat voluntary as censored)
cox_forced <- coxph(Surv(tenure, event_forced) ~ firm_return +
industry_return + founder + outsider +
ceo_age_start + log(firm_size), data = df)
# Voluntary departure (treat forced as censored)
cox_voluntary <- coxph(Surv(tenure, event_voluntary) ~ firm_return +
industry_return + founder + outsider +
ceo_age_start + log(firm_size), data = df)
cat("=== Forced Departure Cox Model ===\n")
print(round(summary(cox_forced)$coefficients[, c(1,2,5)], 4))
cat("\n=== Voluntary Departure Cox Model ===\n")
print(round(summary(cox_voluntary)$coefficients[, c(1,2,5)], 4))
# Compare firm_return coefficients
cat("\n=== Performance Sensitivity by Departure Type ===\n")
cat("Forced HR for firm_return: ",
round(exp(coef(cox_forced)["firm_return"]), 3), "\n")
cat("Voluntary HR for firm_return: ",
round(exp(coef(cox_voluntary)["firm_return"]), 3), "\n")
cat("\nExpected: Forced departures are MORE sensitive to\n")
cat("poor performance than voluntary departures.\n")Step 6: Compare with Published Results
cat("==========================================================\n")
cat("COMPARISON: Our Replication vs. Turnover Literature\n")
cat("==========================================================\n")
cat(sprintf("%-40s %10s %10s\n", "Finding", "Literature", "Ours"))
cat("----------------------------------------------------------\n")
cat(sprintf("%-40s %10s %10.1f\n", "Median CEO tenure (years)",
"~5", median(df$tenure)))
cat(sprintf("%-40s %10s %10.3f\n", "HR of firm_return (all)",
"0.4-0.7", exp(coef(cox4)["firm_return"])))
cat(sprintf("%-40s %10s %10.3f\n", "HR of firm_return (forced)",
"0.2-0.5", exp(coef(cox_forced)["firm_return"])))
cat(sprintf("%-40s %10s %10.3f\n", "Founder protective (HR)",
"0.5-0.8", exp(coef(cox4)["founder"])))
cat("----------------------------------------------------------\n")
cat("Qualitative conclusions confirmed.\n")Read the analysis below carefully and identify the errors.
A researcher estimates a Cox PH model of CEO turnover using a panel of 800 CEO-firm spells. They report:
Cox model: hazard(departure) = h0(t) * exp(b1*stock_return + b2*CEO_age + b3*board_size)
Results: stock_return HR = 0.65, p < 0.01. "A one percent increase in stock returns reduces CEO turnover hazard by 35%. To assess whether boards evaluate CEOs fairly, we also test for relative performance evaluation by adding industry_return. The coefficient on industry_return is insignificant (p = 0.42), confirming that boards use RPE — they filter out industry shocks when evaluating CEO performance."
Select all errors you can find:
Summary
Our replication confirms the key empirical patterns in the CEO turnover literature:
-
Poor firm performance increases CEO departure hazard. CEOs of underperforming firms face a significantly higher hazard of departure, with hazard ratios of 0.50--0.75 for firm stock returns.
-
Imperfect relative performance evaluation. Boards do not fully filter out industry-wide shocks when evaluating CEOs. Market and industry performance also affect turnover, suggesting that CEOs are sometimes punished for factors beyond their control.
-
Competing risks reveal important heterogeneity. The performance-turnover link is concentrated in forced departures. Voluntary departures are driven more by age, tenure, and outside opportunities.
-
The proportional hazards assumption should be tested. The effect of performance on turnover may change over the CEO's tenure, warranting time-varying coefficient models or stratified estimation.
Extension Exercises
-
Time-varying covariates. Restructure the data as a CEO-year panel with annual performance measures. Estimate a Cox model with time-varying firm returns.
-
Fine and Gray competing risks. Estimate subdistribution hazard models (Fine-Gray) and compare with the cause-specific Cox models from Step 5.
-
Frailty models. Add a firm-level frailty (random effect) to account for unobserved heterogeneity in governance quality across firms.
-
Accelerated failure time. Estimate a parametric AFT model (log-normal or log-logistic) and compare the interpretation with the Cox PH model.
-
Board composition. Add board independence, institutional ownership, and CEO duality as governance covariates. Test whether governance moderates the performance-turnover sensitivity.