# F2-dev-01 — Two-way FE slope with firm-clustered uncertainty ## Question Estimate β in y_it = a_i + g_t + β x_it + u_it (firm and year effects unrestricted), with standard errors robust to arbitrary dependence within firm over time. ## Data and exclusions `data/firm_panel.csv` (synthetic), 1200 rows read (`n_input_rows`). Balanced panel of 100 firms (`n_firms`) × 12 years 2008–2019 (`n_years`); no missing values, no duplicate firm–year keys, so no rows were excluded (`out/exclusions.csv`; `data_issues` is empty). All 1200 observations are analysed (`n_analysed`). ## Method - Baseline: pooled OLS of y on x with an intercept (`beta_pooled`). - Two-way FE: OLS with firm and year dummies (LSDV) (`beta_fe`); checked against the two-way within (double-demeaning) estimator, which agrees to <1e-10 (`beta_fe_within_check`). - Conventional SE: homoskedastic OLS SE from the LSDV fit, residual df = N − G − T = 1088 (`se_conventional`). - Clustered SE: firm-clustered sandwich (CR1) via Frisch–Waugh–Lovell, small-sample factor G/(G−1)·(N−1)/(N−K) with K = 12 (slope + 11 year effects; firm effects are nested in clusters and not counted, as in Stata `xtreg, fe` / `reghdfe`) (`se_cluster`, `cluster_small_sample_factor`). - 95% CI: beta_fe ± t_{0.975, G−1} · se_cluster, with G−1 = 99 df (`ci_critical_value_t`, `ci_df`). - No randomness (`random_seed` = null). ## Results - Pooled baseline slope: 1.268 (`beta_pooled`). - Two-way FE slope: 0.6045 (`beta_fe`). - The FE estimate is about half the pooled slope. That gap is consistent with firm and/or year effects being correlated with x, which biases the pooled estimate upward. The data do not identify which of the two kinds of effect drives it. ## Uncertainty - Conventional SE 0.0326 (`se_conventional`); firm-clustered SE 0.0450 (`se_cluster`). The ratio is 1.38 (`se_ratio_cluster_to_conventional`), which points to positive within-firm dependence of the residuals and/or regressor. - 95% CI for β (clustered): [0.515, 0.694] (`beta_ci_low`, `beta_ci_high`). - Sensitivity: if all 112 LSDV parameters are counted in K (intercept, slope, 99 firm and 11 year dummies), as statsmodels does by default on the LSDV fit, the clustered SE is 0.0471 (`se_cluster_lsdv_all_dummies_counted`, matching `se_cluster_statsmodels_lsdv`). This is a convention difference and does not change the conclusions. ## Limitations and open issues - The data are synthetic. β is a within-firm, within-year association; a causal reading would need strict exogeneity of x given the fixed effects, which cannot be tested here. - With G = 100 clusters, cluster-robust inference should be reasonably reliable, but it is still asymptotic. Wild-cluster bootstrap was not run because the task does not require it. - The clustered SE depends on which degrees-of-freedom correction is used, by about 4.5% (see Uncertainty). The primary choice is documented above. - Feasibility: complete (`feasibility`).