Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Contract year effects on xwOBA: replication code

Estimation code for "The Contract Year Phenomenon Revisited" (Atkins & Dikgang, working paper, 2026).

Panel of 3,654 player-seasons, 1,003 MLB hitters, 2015-2025. Two-way fixed effects on xwOBA. The pooled contract-year estimate is indistinguishable from zero; split by career stage, players with five or more years of service are associated with about six points higher xwOBA in contract years while younger players show no comparable response (difference significant at p = .004).

Every table in the paper is produced by this code.

Running it

# put the analysis panel at data/Clean_Data.csv (see data/README.md)
Rscript run_all.R

Base R only. No packages: no plm, no lfe, no fixest, no tidyverse. Takes about a minute, most of it the 2,000-replicate bootstrap.

Writes to output/:

File Contents
contract_year_analysis.log full transcript of the run
contract_year_clean.csv / .rds the constructed panel
results_ledger.csv every estimate, machine readable
manuscript_facts.csv every descriptive figure quoted in the text
tables_manuscript.tex Tables 1-8, ready to \input
table1-table8, tableA1 individual .tex / .csv exports

Layout

The pipeline is the nine scripts in R/, run in order by run_all.R. contract_year.R is the same code as a single file, generated by build_single_file.R; the two are verified to produce byte-identical output. Edit R/ and rebuild rather than patching the single file.

run_all.R                 orchestrator; set PROJ, VET_CUTOFF, BOOT_B here
contract_year.R           whole pipeline in one file (generated)
build_single_file.R       builds contract_year.R from R/

R/00_functions.R          felm, wald, tost, CP850 reader
R/01_build.R              panel construction, data gate
R/02_models.R             all estimates
R/03_tables.R             machine-readable exports
R/04_verify.R             56-check estimate gate
R/05_table7.R             career-stage ladder, bootstrap
R/06_manuscript.R         descriptive figures quoted in the text
R/07_manuscript_tables.R  Tables 1-8 in the paper's LaTeX style
R/08_endogeneity.R        selection into contract-year status

data/                     not tracked; see data/README.md
output/                   not tracked; generated

Specification

xwOBA_it = a_i + g_t + b*ContractYear_it + d1*MLS_it + d2*MLS_it^2 + u_it

Player and season fixed effects, standard errors clustered on player, so b is identified from within-player variation in contract status. The pipeline also runs a lead-lag model overall and by career stage, a three-rung interaction ladder for the career-stage difference, equivalence tests and minimum detectable effects, disaggregation by contract type, survivorship checks, and tests of selection into contract-year status.

felm()

R/00_functions.R implements clustered fixed-effects estimation directly, reproducing Stata's xtreg, fe cluster(). Two things are easy to get wrong:

  • K in the small-sample adjustment excludes the absorbed fixed effects. Including them, which is what areg does, gives standard errors about 17% larger here. Player effects are nested within player clusters, so xtreg is the right convention (Cameron & Miller, 2015). felm(..., dfadj = TRUE) switches, and the pipeline reports both.
  • Tests use df = G - 1, not N - K.

The career-stage difference is also bootstrapped over players, which avoids the degrees-of-freedom question entirely.

Verification

An earlier build of this analysis returned the opposite sign on a key coefficient with the correct N and player count, because one service-time value was wrong. Sample size is not a check. The pipeline gates itself three times and will not write tables if a gate fails:

  1. R/01_build.R checks means, sums and counts of every constructed variable against known values, and checks that service time never decreases. Runs before any model is fit.
  2. R/04_verify.R checks 56 coefficients, standard errors, p-values and descriptive counts. It also checks that the fully interacted pooled specification reproduces the split-sample difference, which it does to about 1.6e-14.
  3. R/06_manuscript.R checks the nine lead-lag cells feeding Table 3 before the table is written.

The build also repairs itself: two extracts of the source file circulate, differing in one service-time value, and 01_build.R detects and corrects the bad one rather than stopping, so either input gives the same output and the run reports which it found.

Data

Not included. It comes from FanGraphs and Cot's Baseball Contracts, neither of which is mine to redistribute. data/README.md has the sources, the schema, the sample definition, the service-time encoding and the one known correction, so the panel can be rebuilt and checked against the fingerprint gate.

Citation

@unpublished{atkins_dikgang_2026,
  author = {Atkins, Matt and Dikgang, Johane},
  title  = {The Contract Year Phenomenon Revisited},
  note   = {Working paper},
  year   = {2026}
}

License

Code is MIT (see LICENSE), written by Matt Atkins. The paper is joint work and is not distributed here. The data is not mine to license.

About

Replication code for a fixed-effects study of contract-year performance in MLB

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages