FIRC0002 Stage F NB-01..11 (verifier's non-blocking items): VW-weight universes made explicit, ordinals generated, bib/hook housekeeping, Table 3 max named
NB-1 scripts/r3k_20_tables_v2.py: the VW concentration diagnostic is emitted for two universes read from
results/r3k_v2/vw_weights_diag.csv and asserted equal to results.json vw_diag -- all nine full-sample VW
sorts (max 36 %, cybersecurity placebo sort, AAPL, 2023 cohort, quintile 2; 18 entries = 9 distinct cells
above 25 %) and the composite sort alone (max 19 %, AAPL, 2011 cohort, quintile 4; no cell above 25 %).
New macros \mvwMaxWeightAll/AllSort/AllCell, \mvwCellsAboveAll/AllDistinct, \mvwSortsAboveAll,
\mvwMaxWeightCompos/ComposCell, \mvwCellsAboveCompos (+ v2.0 composite values). Stated in the Table 4
note (paper/tables/tab_sorts_both.tex), the appendix sorts note (tab_sorts.tex), two rows of the hygiene
Panel B (tab_hygiene.tex), the data-hygiene sentence (paper/sections/data.tex) and README.md.
NB-4 scripts/stageE_writing_macros.py: ordinal() helper, *Ord macros emitted with the ranks/percentiles
(\mIdaRankOrd ... \mHeRankCoreOrd, \mUtilSectorRankOrd, \mProbe*PctOrd); r3k_20 adds \mplaceboRankOrd/
\mplaceboRankWOrd. Hard-coded suffixes removed from robustness.tex, data.tex, literature.tex, results.tex.
NB-5 paper/sections/literature.tex: eisfeldt2023generative cited as the first circulated version of the JF 2026
paper (entry kept, now printed in the bibliography).
NB-6 paper/main.tex: redundant threeparttable -> adjustbox{max width} hook removed; build unchanged
(92 pages, 0 errors, 0 undefined, 2 overfull boxes of 4.42 pt, none > 5 pt).
NB-11 scripts/r3k_20_tables_v2.py: Table 3 note names the firm-year of the maximum market-adjusted forward
return (GME, formation year 2019, window 2020-06-30 to 2021-06-30, verdict confirmed on FinData OS bars),
read from the panel and hygiene_log.csv; macros \mmaxMadjRetTicker/Year/Pct.
NB-2 (reproducibility/FIRC-0002/validation_report.md), NB-9 (jof250 parser note), NB-3/7/8/10 (author or venue
decisions) are recorded outside the worktree (improvements/FIRC-0002/unresolved_issues.md "Stage F",
manuscripts/FIRC-0002/change_log_{writing,exhibits}.md).
Build: latexmk clean, rc 0; tests/test_regenerate.py --quick OK; paper/main.pdf rebuilt (92 pages).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
16 changed files +120 −35
modified
README.md
+2 −1
@@ -21,7 +21,8 @@ | ||
| 21 | 21 | | Object | Estimate (v2.1) | |
| 22 | 22 | |---|---| |
| 23 | 23 | | Panel | 27,354 firm-years, 2,375 firms, formation years 2010–2024 (6,719 firm-years of S&P 500 members); corrected market capitalization, cleaned returns (14 series breaks removed), CIK key | |
| 24 | −| Full-sample Q5–Q1 spread (composite) | EW raw +5.5 (NW t 2.40); EW winsorized +3.9 (NW t 2.00, plain 1.78, CI [-0.8, +8.6]); median +0.5; VW (corrected cap) +7.4 (t 2.69); S&P 500 members EW winsorized +5.7 | | |
| 24 | +| Full-sample Q5–Q1 spread (composite) | EW raw +5.5 (NW t 2.40); EW winsorized +3.9 (NW t 2.00, plain 1.78, CI [-0.8, +8.6]); median +0.5; VW (corrected cap) +7.4 (t 2.69; 10 %-capped +7.2, t 2.62); S&P 500 members EW winsorized +5.7 | | |
| 25 | +| VW concentration (single-firm weight in a cohort × quintile cell) | composite sort: max 19 % (AAPL, 2011 cohort, quintile 4), no cell above 25 % (the Stage D criterion holds for the composite); across all nine VW sorts: max 36 % in the cybersecurity placebo sort (AAPL, 2023 cohort, quintile 2), 9 distinct cells above 25 %, all in the cybersecurity and SPID sorts (18 entries counting the raw and winsorized variants; committed v2.0 capitalization: 100 %, 548) | | |
| 25 | 26 | | Fama–MacBeth slope (composite) | winsorized +1.88 pp/SD (NW t 2.41, plain 2.51, CI [+0.27, +3.49]); raw +2.77 (t 2.44); with sector dummies, winsorized +0.88 (NW 2.80, plain 2.74); sector×year FE +0.75 (firm t 2.04, two-way 2.78) | |
| 26 | 27 | | Pre / post ChatGPT, Russell 3000 (winsorized) | +3.4 → +6.0 (post CI [-25.5, +37.6], n = 3); cohorts 2022: -5.0, 2023: +3.1, 2024: +19.9; pooled post +6.2 (t 2.14 firm / 0.84 two-way, wild p 0.631); post − pre +2.4 (t 0.78 / 0.36, MDE 8.5); permutation p 0.31; sup-Wald p 0.59; post FF6 alpha -0.3 %/yr | |
| 27 | 28 | | Pre / post, S&P 500 members (winsorized) | +3.8 → +13.2; cohorts 2022: +14.8, 2023: -7.1, 2024: +32.0; pooled post +13.3 (t 2.39 / 1.15); post Sharpe 0.69 | |
modified
paper/main.pdf
+0 −0
Binary file not shown.
modified
paper/main.tex
+5 −12
@@ -96,19 +96,12 @@ | ||
| 96 | 96 | \sisetup{group-minimum-digits=4, group-separator={,}} |
| 97 | 97 | \newcommand{\tstat}[1]{{\footnotesize(\textit{t}=#1)}} |
| 98 | 98 | \newcommand{\tablelegend}[1]{\par\vspace{0.25em}\begin{minipage}{\linewidth}\footnotesize\textit{Notes:} #1\end{minipage}} |
| 99 | −% Generated table fragments carry their own \footnotesize notes paragraph after the tabular. | |
| 100 | −% Scaling the whole fragment with \adjustbox would put the notes on one scaled line, so every | |
| 101 | −% tabular of the document is wrapped, by an etoolbox hook, in adjustbox{max width=\textwidth}: | |
| 102 | −% a tabular wider than the text block is shrunk, a narrower one is untouched, and the notes | |
| 103 | −% paragraph is typeset at \textwidth. \widetable is kept as a marker for the fragments that | |
| 104 | −% needed scaling before the hook existed. | |
| 105 | −% The generated fragments wrap each tabular in a threeparttable (which redefines \tabular, so a | |
| 106 | −% hook on tabular would be bypassed); the hook is therefore attached to threeparttable. A fragment | |
| 107 | −% whose natural width exceeds \textwidth is shrunk with its notes (the exhibits agent is asked to | |
| 108 | −% narrow such fragments at generation; the hook keeps the build free of overfull boxes meanwhile). | |
| 99 | +% Generated table fragments (scripts/r3k_20_tables_v2.py) are self-contained threeparttable floats | |
| 100 | +% whose natural width fits the text block (34/34 in the Stage E width scan), so no scaling hook is | |
| 101 | +% needed: the threeparttable -> adjustbox{max width=\textwidth} hook of Stage E was removed at | |
| 102 | +% Stage F (NB-6) with the build unchanged (0 overfull boxes > 5 pt). \widetable is kept as an | |
| 103 | +% inert marker for legacy fragments. | |
| 109 | 104 | \usepackage{etoolbox} |
| 110 | −\BeforeBeginEnvironment{threeparttable}{\begin{adjustbox}{max width=\textwidth}} | |
| 111 | −\AfterEndEnvironment{threeparttable}{\end{adjustbox}} | |
| 112 | 105 | \newcommand{\widetable}[1]{#1} |
| 113 | 106 | |
| 114 | 107 | % --- Misc --- |
modified
paper/sections/data.tex
+8 −4
@@ -60,7 +60,7 @@ where $\mathbf e_c$ is the embedding of the chunk. Because similarity is compute | ||
| 60 | 60 | space, a passage on ``deploying neural networks to forecast demand'' scores highly on AI adoption |
| 61 | 61 | even though it shares no keyword with the anchor; in a probe of written sentences, paraphrases |
| 62 | 62 | without any AI keyword score \mProbeParaphraseAdopt{} on the adoption anchor, above the |
| 63 | −\mProbeParaphrasePct{}th percentile of corpus chunks. The eight concepts comprise three AI | |
| 63 | +\mProbeParaphrasePctOrd{} percentile of corpus chunks. The eight concepts comprise three AI | |
| 64 | 64 | facets---\emph{AI adoption}, \emph{AI infrastructure} and \emph{AI disruption risk}---and five |
| 65 | 65 | placebos: \emph{ESG commitment}, \emph{climate risk}, \emph{cybersecurity}, |
| 66 | 66 | \emph{automation/labour} and \emph{macro risk}. Two features of the anchors matter for |
@@ -195,8 +195,12 @@ them Apple, Microsoft and Nvidia in 2022--24), the share of firm-years without a | ||
| 195 | 195 | falls from \mShareNoCapVone\% to \mShareNoCapVtwo\%, and \msizeGroupChange\% of firm-years change |
| 196 | 196 | size group. The value-weighted exhibits of the first version were single-stock artefacts---the |
| 197 | 197 | largest weight in a cohort-by-quintile cell was \mvwMaxWeightVone\%---and are replaced |
| 198 | −(\cref{subsec:sorts}); the largest weight is now \mvwMaxWeight\%, which is genuine mega-cap | |
| 199 | −dominance, so a weight-capped variant is reported beside the value-weighted rows. Daily prices | |
| 198 | +(\cref{subsec:sorts}); in the composite sort's own cells the largest weight is now | |
| 199 | +\mvwMaxWeightCompos\% (\mvwMaxWeightComposCell) and no cell exceeds 25\%, and the largest weight | |
| 200 | +across all nine value-weighted sorts is \mvwMaxWeightAll\%, in the \mvwMaxWeightAllSort{} sort | |
| 201 | +(\mvwMaxWeightAllCell), with \mvwCellsAboveAllDistinct{} cells above 25\%, all of them in the | |
| 202 | +\mvwSortsAboveAll{} sorts; this is genuine mega-cap dominance, not a data error, so a | |
| 203 | +weight-capped variant is reported beside the value-weighted rows. Daily prices | |
| 200 | 204 | from January 2015 to June 2024 are used for the event study and its placebo launches, with IWV as |
| 201 | 205 | the market index and the daily Fama--French factors for the calendar-time benchmark. |
| 202 | 206 | |
@@ -271,7 +275,7 @@ design compares each firm to its contemporaneous peers and removes both facts. | ||
| 271 | 275 | enterprise-technology and software firms, data-intensive services and industrial firms, but also |
| 272 | 276 | two electric utilities (IDACORP, rank \mIdaRank, and Hawaiian Electric, rank \mHeRank), whose |
| 273 | 277 | filings describe data-center load growth and generation capacity rather than AI. Utilities are |
| 274 | −the sector with the \mUtilSectorRank{}nd-highest mean composite (\mSectorZUtil{} standard | |
| 278 | +the sector with the \mUtilSectorRankOrd{}-highest mean composite (\mSectorZUtil{} standard | |
| 275 | 279 | deviations, after Information Technology at \mSectorZIT) although \mKwUtilShareZero\% of their |
| 276 | 280 | firm-years contain no core AI term; \cref{subsec:construct} returns to this. |
| 277 | 281 | |
modified
paper/sections/literature.tex
+2 −2
@@ -45,7 +45,7 @@ than assumes. The first version of this paper claimed that the embedding separat | ||
| 45 | 45 | currently use artificial intelligence'' from ``we are an artificial-intelligence leader''; a |
| 46 | 46 | direct probe shows that it does so only in part---negated sentences score lower than affirmative |
| 47 | 47 | ones (\mProbeNegated{} against \mProbeAffirm{} on the adoption anchor) but still above the |
| 48 | −\mProbeNegatedPct{}th percentile of corpus chunks---so the claim is withdrawn. Sentence encoders | |
| 48 | +\mProbeNegatedPctOrd{} percentile of corpus chunks---so the claim is withdrawn. Sentence encoders | |
| 49 | 49 | of the MiniLM class are known to be weakly sensitive to negation, and the measure should be read |
| 50 | 50 | as topical proximity to AI vocabulary rather than as a stance. The literature also warns that |
| 51 | 51 | text-based measures inherit the composition of the corpus they are computed on |
@@ -75,7 +75,7 @@ investment from job postings and r\'esum\'es and document faster growth among AI | ||
| 75 | 75 | \citet{acemoglu2022aijobs}, \citet{webb2020} and \citet{hampole2025ai} measure the exposure of |
| 76 | 76 | occupations, establishments and firms' workforces to AI; \citet{acemoglu2020wrong} study the |
| 77 | 77 | labour-market effects of industrial robots. Three papers of 2026 are the closest to ours and were |
| 78 | −absent from the first version. \citet{eisfeldt2026generative} construct a firm-level measure of | |
| 78 | +absent from the first version. \citet{eisfeldt2026generative}, first circulated as \citet{eisfeldt2023generative}, construct a firm-level measure of | |
| 79 | 79 | \emph{generative}-AI exposure from the task content of occupations, show that a long--short |
| 80 | 80 | portfolio on that measure earned large returns in the two weeks after ChatGPT's release, and |
| 81 | 81 | separate this workforce exposure from firms' \emph{product} exposure to AI. \citet{basu2026aiexposure} |
modified
paper/sections/macros_stageE.tex
+12 −0
@@ -57,6 +57,18 @@ | ||
| 57 | 57 | \newcommand{\mHeRank}{11} |
| 58 | 58 | \newcommand{\mHeRankStrip}{21} |
| 59 | 59 | \newcommand{\mHeRankCore}{63} |
| 60 | +\newcommand{\mIdaRankOrd}{4th} | |
| 61 | +\newcommand{\mIdaRankStripOrd}{4th} | |
| 62 | +\newcommand{\mIdaRankCoreOrd}{7th} | |
| 63 | +\newcommand{\mHeRankOrd}{11th} | |
| 64 | +\newcommand{\mHeRankStripOrd}{21st} | |
| 65 | +\newcommand{\mHeRankCoreOrd}{63rd} | |
| 66 | +\newcommand{\mUtilSectorRankOrd}{2nd} | |
| 67 | +\newcommand{\mProbeNegatedPctOrd}{99.8th} | |
| 68 | +\newcommand{\mProbeUtilityPctOrd}{98.1th} | |
| 69 | +\newcommand{\mProbeParaphrasePctOrd}{99.8th} | |
| 70 | +\newcommand{\mProbeCyberPctOrd}{94.0th} | |
| 71 | +\newcommand{\mProbeUnrelatedPctOrd}{43rd} | |
| 60 | 72 | \newcommand{\mTopThirtyUtilOriginal}{3} |
| 61 | 73 | \newcommand{\mTopThirtyUtilCore}{2} |
| 62 | 74 | \newcommand{\mTopThirtyOverlapStrip}{22} |
modified
paper/sections/results.tex
+1 −1
@@ -132,7 +132,7 @@ rises after the launch, the broad-universe series does not. The pooled slope-cha | ||
| 132 | 132 | cohort spread of the sample is 2018. Three tests ask whether 2022 is a special break date. The |
| 133 | 133 | illustrative placebo of the first version (\cref{fig:placebobreaks}), which computes the |
| 134 | 134 | post-minus-pre difference for each candidate first post-cohort from 2013 to 2022, ranks 2022 |
| 135 | −\mplaceboRankW{}th of \mplaceboN{}; because its windows are nested it is not a test. The exact | |
| 135 | +\mplaceboRankWOrd{} of \mplaceboN{}; because its windows are nested it is not a test. The exact | |
| 136 | 136 | permutation of the 2022--24 block against all \mPermSubsets{} three-cohort subsets gives a |
| 137 | 137 | one-sided $p$-value of \mpermPW{} (\mpermP{} on raw returns), and \mContigAboveW{} of the |
| 138 | 138 | \mContigN{} contiguous three-cohort blocks exceed the 2022--24 block (\mContigAboveRaw{} on raw |
modified
paper/sections/robustness.tex
+5 −5
@@ -240,7 +240,7 @@ eleven sectors (\mSectorZUtil{} standard deviations) with a keyword correlation | ||
| 240 | 240 | \mKwUtilSpearman{} and \mKwUtilShareZero\% of firm-years without any AI term, and they are |
| 241 | 241 | \mUtilShareQfive\% of top-quintile firm-years against \mUtilShareAll\% of the sample; the probe |
| 242 | 242 | confirms that sentences about data-center load growth and generation capacity score |
| 243 | −\mProbeUtilInfra{} on the infrastructure anchor, above the \mProbeUtilityPct{}th percentile of | |
| 243 | +\mProbeUtilInfra{} on the infrastructure anchor, above the \mProbeUtilityPctOrd{} percentile of | |
| 244 | 244 | corpus chunks. Excluding Utilities and Energy leaves the composite spread at \mExUtilFullW{} |
| 245 | 245 | points ($t=\mExUtilFullWT$). Two alternative anchor sets were scored against the stored |
| 246 | 246 | embeddings (no corpus re-embedding): anchors stripped of the cybersecurity and automation |
@@ -248,14 +248,14 @@ vocabulary give a composite with rank correlation \mStripRho{} to the baseline, | ||
| 248 | 248 | \mStripFullW{} points ($t=\mStripFullWT$) and a Fama--MacBeth slope of \mStripFMW{} |
| 249 | 249 | ($t=\mStripFMWT$); a core-AI adoption anchor gives \mCoreRho, \mCoreFullW{} ($t=\mCoreFullWT$) and |
| 250 | 250 | \mCoreFMW{} ($t=\mCoreFMWT$); a cybersecurity anchor stripped of AI terms gives \mCyberStripFullW{} |
| 251 | −($t=\mCyberStripFullWT$). IDACORP ranks \mIdaRank{}th on the baseline composite, \mIdaRankStrip{}th | |
| 252 | −on the stripped anchors and \mIdaRankCore{}th on the core anchor; Hawaiian Electric \mHeRank{}th, | |
| 253 | −\mHeRankStrip{}st and \mHeRankCore{}rd. The results are therefore not driven by the shared | |
| 251 | +($t=\mCyberStripFullWT$). IDACORP ranks \mIdaRankOrd{} on the baseline composite, \mIdaRankStripOrd{} | |
| 252 | +on the stripped anchors and \mIdaRankCoreOrd{} on the core anchor; Hawaiian Electric \mHeRankOrd{}, | |
| 253 | +\mHeRankStripOrd{} and \mHeRankCoreOrd{}. The results are therefore not driven by the shared | |
| 254 | 254 | vocabulary of the anchors, but the construct remains proximity to a technology-and-infrastructure |
| 255 | 255 | register rather than validated AI activity. Finally, the negation probe of \cref{subsec:lit_text}: |
| 256 | 256 | negated AI sentences score \mProbeNegated{} on the adoption anchor against \mProbeAffirm{} for |
| 257 | 257 | affirmative ones, a difference of \mProbeDiff{} that holds for \mProbePairsShare\% of |
| 258 | −sentence pairs, but both lie above the \mProbeNegatedPct{}th percentile of corpus chunks, so the | |
| 258 | +sentence pairs, but both lie above the \mProbeNegatedPctOrd{} percentile of corpus chunks, so the | |
| 259 | 259 | score distinguishes topic far better than stance. |
| 260 | 260 | |
| 261 | 261 | \ExhibitOnce{tab:constructvalidity}{\input{tables/tab_construct_validity}} |
modified
paper/tables/macros.tex
+16 −0
@@ -12,6 +12,9 @@ | ||
| 12 | 12 | \makeatother |
| 13 | 13 | % ---- Number macros: every value below is read from a result file by scripts/r3k_20_tables_v2.py; legacy names keep the legacy |
| 14 | 14 | % statistic (e.g. \meventT is the Welch t), the robust versions carry suffixes W / CDA / Ttw / CI / MDE / Pwild. ---- |
| 15 | +\newcommand{\mmaxMadjRetTicker}{GME} | |
| 16 | +\newcommand{\mmaxMadjRetYear}{2019} | |
| 17 | +\newcommand{\mmaxMadjRetPct}{+4{,}793} | |
| 15 | 18 | \newcommand{\mnObs}{27{,}354} |
| 16 | 19 | \newcommand{\mnFirms}{2{,}375} |
| 17 | 20 | \newcommand{\mnFirmYearsPanel}{32{,}818} |
@@ -179,6 +182,8 @@ | ||
| 179 | 182 | \newcommand{\mplaceboRank}{8} |
| 180 | 183 | \newcommand{\mplaceboN}{10} |
| 181 | 184 | \newcommand{\mplaceboRankW}{8} |
| 185 | +\newcommand{\mplaceboRankOrd}{8th} | |
| 186 | +\newcommand{\mplaceboRankWOrd}{8th} | |
| 182 | 187 | \newcommand{\mpermP}{0.28} |
| 183 | 188 | \newcommand{\mpermPW}{0.31} |
| 184 | 189 | \newcommand{\mpermRank}{126} |
@@ -298,6 +303,17 @@ | ||
| 298 | 303 | \newcommand{\mewQfive}{+23.1} |
| 299 | 304 | \newcommand{\mvwMaxWeight}{36} |
| 300 | 305 | \newcommand{\mvwMaxWeightVone}{100.0} |
| 306 | +\newcommand{\mvwMaxWeightAll}{36} | |
| 307 | +\newcommand{\mvwMaxWeightAllSort}{cybersecurity placebo} | |
| 308 | +\newcommand{\mvwMaxWeightAllCell}{AAPL, 2023 cohort, quintile 2} | |
| 309 | +\newcommand{\mvwCellsAboveAll}{18} | |
| 310 | +\newcommand{\mvwCellsAboveAllDistinct}{9} | |
| 311 | +\newcommand{\mvwSortsAboveAll}{SPID technology and cybersecurity placebo} | |
| 312 | +\newcommand{\mvwMaxWeightCompos}{19} | |
| 313 | +\newcommand{\mvwMaxWeightComposCell}{AAPL, 2011 cohort, quintile 4} | |
| 314 | +\newcommand{\mvwCellsAboveCompos}{0} | |
| 315 | +\newcommand{\mvwMaxWeightComposVone}{100.0} | |
| 316 | +\newcommand{\mvwCellsAboveComposVone}{31} | |
| 301 | 317 | \newcommand{\mfeCompos}{-0.07} |
| 302 | 318 | \newcommand{\mfeComposT}{-0.12} |
| 303 | 319 | \newcommand{\mfeAdopt}{-0.21} |
modified
paper/tables/tab_hygiene.tex
+2 −1
@@ -28,7 +28,8 @@ Statistic & \multicolumn{2}{l}{BEFORE (committed v2.0)} & \multicolumn{3}{l}{AFT | ||
| 28 | 28 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Panel: firm-years / firms / no capitalization} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{27{,}468 / 2{,}379 / 2{,}168} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{27{,}354 / 2{,}375 / 308} \\ |
| 29 | 29 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{log capitalization range (min, max)} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{[$-\infty$, 37.4]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{[15.5, 29.0]} \\ |
| 30 | 30 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{AAPL capitalization, June 2011 (USD bn; rank)} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{9.3 (rank 166)} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{310 (rank 2)} \\ |
| 31 | −\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile; cells $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\%; 548} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{36\%; 18} \\ | |
| 31 | +\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile, all nine VW sorts (raw and wins.\ variants); entries $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\% (AJG); 548} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{36\% (cybersecurity placebo sort, AAPL, 2023 cohort, quintile 2); 18} \\ | |
| 32 | +\multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Max single-firm VW weight in a cohort $\times$ quintile, composite sort; cells $>$ 25\%} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{100.0\% (AJG); 31} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{19\% (AAPL, 2011 cohort, quintile 4); 0} \\ | |
| 32 | 33 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{EW composite Q5$-$Q1, full (pp) [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+5.5 [2.41]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+5.5 [2.40] $\rightarrow$ +3.9 [2.00; plain 1.78], CI [-0.8, +8.6]; median +0.5 [0.29]} \\ |
| 33 | 34 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{VW composite: Q1 / Q5 / Q5$-$Q1 [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+0.4 / +14.3 / +13.9 [2.59]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+12.1 / +19.4 / +7.4 [2.69] $\rightarrow$ wins.\ +7.3 [2.68]} \\ |
| 34 | 35 | \multicolumn{1}{>{\raggedright\arraybackslash}p{0.25\textwidth}}{Fama--MacBeth composite slope (pp/SD) [NW $t$]} & \multicolumn{2}{>{\raggedright\arraybackslash}p{0.19\textwidth}}{+3.07 [2.80]} & \multicolumn{3}{>{\raggedright\arraybackslash}p{0.39\textwidth}}{+2.77 [2.44] $\rightarrow$ +1.88 [2.41; plain 2.51], CI [+0.27, +3.49]} \\ |
modified
paper/tables/tab_sorts.tex
+1 −1
@@ -57,6 +57,6 @@ SPID projects (legacy quintiles) & EW, wins. & +16.9 & +17.7 & +18.7 & +18.7 & + | ||
| 57 | 57 | \bottomrule |
| 58 | 58 | \end{tabular} |
| 59 | 59 | \begin{tablenotes}[flushleft]\scriptsize |
| 60 | −\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\ref{tab:spidbinary} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\times$ point-in-time shares); maximum single-firm weight 36\%. | |
| 60 | +\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\ref{tab:spidbinary} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\times$ point-in-time shares). Maximum single-firm weight in a cohort $\times$ quintile cell: 36\% across the nine VW sorts of this table (cybersecurity placebo sort: AAPL, 2023 cohort, quintile 2; 9 distinct cells above 25\%, all in the SPID technology and cybersecurity placebo sorts) and 19\% in the composite sort (AAPL, 2011 cohort, quintile 4; no cell above 25\%). | |
| 61 | 61 | \end{tablenotes} |
| 62 | 62 | \end{threeparttable}} |
modified
paper/tables/tab_sorts_both.tex
+1 −1
@@ -36,6 +36,6 @@ PC1 of three facets & EW, wins. & +16.1 & +16.3 & +17.5 & +19.1 & +21.7 & +5.7 & | ||
| 36 | 36 | \bottomrule |
| 37 | 37 | \end{tabular} |
| 38 | 38 | \begin{tablenotes}[flushleft]\scriptsize |
| 39 | −\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\ spread of +3.9 pp per year compares with a Q1 mean of +16.4\% per year; the top 1\% of returns contribute +2.5 pp of the raw spread. VW diagnostic: maximum single-firm weight in any cohort $\times$ quintile cell 36\% (committed v2.0 capitalization: 100.0\%); cells above 25\%: 18 (v2.0: 548); the 10\%-capped VW composite spread is +7.2 pp (NW $t$ 2.62). Romano--Wolf adjusted $p$ of the composite EW wins.\ spread within the ten-member family of this table: 0.32 (Table~\ref{tab:mht}). All signals and weightings: Table~\ref{tab:sorts}. | |
| 39 | +\item \emph{Notes:} Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; 95\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns (July $Y{+}1$--June $Y{+}2$); `wins.' = 1/99 winsorized within the formation year; `median' = median spread within cohort; `raw' = unwinsorized. Rebuilt panel: $N$ = 27{,}354 firm-years, 2{,}375 firms, keyed on CIK $\times$ year, cleaned returns, corrected market capitalization. No significance stars are used. Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\ spread of +3.9 pp per year compares with a Q1 mean of +16.4\% per year; the top 1\% of returns contribute +2.5 pp of the raw spread. VW diagnostic (single-firm weight in a cohort $\times$ quintile cell): in the composite sort's own cells the maximum is 19\% (AAPL, 2011 cohort, quintile 4) and no cell exceeds 25\%; across all nine full-sample VW sorts of Table~\ref{tab:sorts} the maximum is 36\% (cybersecurity placebo sort: AAPL, 2023 cohort, quintile 2) and 18 entries exceed 25\% (9 distinct cells, each counted once for the raw and once for the winsorized return variant); committed v2.0 capitalization: 100.0\% and 548 entries (274 distinct cells); the 10\%-capped VW composite spread is +7.2 pp (NW $t$ 2.62). Romano--Wolf adjusted $p$ of the composite EW wins.\ spread within the ten-member family of this table: 0.32 (Table~\ref{tab:mht}). All signals and weightings: Table~\ref{tab:sorts}. | |
| 40 | 40 | \end{tablenotes} |
| 41 | 41 | \end{threeparttable}} |
modified
paper/tables/tab_summary.tex
+1 −1
@@ -21,6 +21,6 @@ Text chunks per firm-year (count) & 27{,}354 & 330 & 154 & 1 & 68 & 306 & 866 & | ||
| 21 | 21 | \bottomrule |
| 22 | 22 | \end{tabular} |
| 23 | 23 | \begin{tablenotes}[flushleft]\scriptsize |
| 24 | −\item \emph{Notes:} Market capitalization = unadjusted FinData OS close $\times$ point-in-time SEC cover-page shares (log range [15.5, 29.0]); 308 firm-years (1.1\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had 2{,}168). Return hygiene: 48 monthly returns above 300\% and 104 firm-year forward returns above 500\% were checked against FinData OS daily bars; 11 monthly and 3 firm-year observations were price splices and are set to missing (Table~\ref{tab:hygiene}), the others are confirmed. The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. Source: data/processed\_r3k\_v2/analysis\_panel\_v2.parquet, results/r3k\_v2/t1\_summary\_stats.csv. | |
| 24 | +\item \emph{Notes:} Market capitalization = unadjusted FinData OS close $\times$ point-in-time SEC cover-page shares (log range [15.5, 29.0]); 308 firm-years (1.1\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had 2{,}168). Return hygiene: 48 monthly returns above 300\% and 104 firm-year forward returns above 500\% were checked against FinData OS daily bars; 11 monthly and 3 firm-year observations were price splices and are set to missing (Table~\ref{tab:hygiene}), the others are confirmed. The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. The maximum of the market-adjusted forward return (47.93, i.e.\ +4{,}793\%) is GME in formation year 2019 (holding window 2020-06-30 to 2021-06-30), also the maximum of the raw return; its second-source verdict is `confirmed' (FinData OS daily bars, 2020-06-30 1.085 $\to$ 2021-06-30 53.53). Source: data/processed\_r3k\_v2/analysis\_panel\_v2.parquet, results/r3k\_v2/t1\_summary\_stats.csv, hygiene\_log.csv. | |
| 25 | 25 | \end{tablenotes} |
| 26 | 26 | \end{threeparttable}} |
modified
results/r3k_v2/inline_numbers.json
+16 −0
@@ -1,4 +1,7 @@ | ||
| 1 | 1 | { |
| 2 | + "maxMadjRetTicker": "GME", | |
| 3 | + "maxMadjRetYear": "2019", | |
| 4 | + "maxMadjRetPct": "+4{,}793", | |
| 2 | 5 | "nObs": "27{,}354", |
| 3 | 6 | "nFirms": "2{,}375", |
| 4 | 7 | "nFirmYearsPanel": "32{,}818", |
@@ -166,6 +169,8 @@ | ||
| 166 | 169 | "placeboRank": "8", |
| 167 | 170 | "placeboN": "10", |
| 168 | 171 | "placeboRankW": "8", |
| 172 | + "placeboRankOrd": "8th", | |
| 173 | + "placeboRankWOrd": "8th", | |
| 169 | 174 | "permP": "0.28", |
| 170 | 175 | "permPW": "0.31", |
| 171 | 176 | "permRank": "126", |
@@ -285,6 +290,17 @@ | ||
| 285 | 290 | "ewQfive": "+23.1", |
| 286 | 291 | "vwMaxWeight": "36", |
| 287 | 292 | "vwMaxWeightVone": "100.0", |
| 293 | + "vwMaxWeightAll": "36", | |
| 294 | + "vwMaxWeightAllSort": "cybersecurity placebo", | |
| 295 | + "vwMaxWeightAllCell": "AAPL, 2023 cohort, quintile 2", | |
| 296 | + "vwCellsAboveAll": "18", | |
| 297 | + "vwCellsAboveAllDistinct": "9", | |
| 298 | + "vwSortsAboveAll": "SPID technology and cybersecurity placebo", | |
| 299 | + "vwMaxWeightCompos": "19", | |
| 300 | + "vwMaxWeightComposCell": "AAPL, 2011 cohort, quintile 4", | |
| 301 | + "vwCellsAboveCompos": "0", | |
| 302 | + "vwMaxWeightComposVone": "100.0", | |
| 303 | + "vwCellsAboveComposVone": "31", | |
| 288 | 304 | "feCompos": "-0.07", |
| 289 | 305 | "feComposT": "-0.12", |
| 290 | 306 | "feAdopt": "-0.21", |
modified
scripts/r3k_20_tables_v2.py
+34 −6
@@ -36,6 +36,9 @@ def pv(p): return "--" if p is None or (isinstance(p, float) and np.isnan(p)) el | ||
| 36 | 36 | def pct(x, d=1): return "--" if x is None else f"{x * 100:.{d}f}\\%" |
| 37 | 37 | def br(t): return "--" if t is None or (isinstance(t, float) and np.isnan(t)) else f"[{t:.2f}]" # t in brackets |
| 38 | 38 | def tex(s): return str(s).replace("&", "\\&").replace("%", "\\%").replace("_", "\\_") |
| 39 | +def ordinal(x): | |
| 40 | + """Rank -> '4th', '21st', '63rd', '11th' (Stage F, NB-4: the suffix is emitted with the rank so the two cannot diverge).""" | |
| 41 | + k = int(x); return f"{k}{'th' if 10 <= k % 100 <= 20 else {1: 'st', 2: 'nd', 3: 'rd'}.get(k % 10, 'th')}" | |
| 39 | 42 | MAC = {}; EXHIBITS = [] |
| 40 | 43 | TABLE_NOTE_SE = ("Inference: NW $t$ = Newey--West (Bartlett kernel, 3 lags) $t$-statistic of the mean of the annual cohort series; plain $t$ = i.i.d. $t$ over cohorts; " |
| 41 | 44 | "95\\% CI = $t$-interval over cohorts (the post period has 3 cohorts, so its CIs are wide by construction). Returns are the look-ahead-free twelve-month forward returns " |
@@ -71,12 +74,19 @@ lab = {"ai_adoption_wmean": ("AI adoption score (section-weighted cosine)", 4), | ||
| 71 | 74 | body = "" |
| 72 | 75 | for k, (l, d) in lab.items(): |
| 73 | 76 | r = s.loc[k]; body += f"{l} & {n(r['count'])} & {r['mean']:.{d}f} & {r['std']:.{d}f} & {r['min']:.{d}f} & {r['1%']:.{d}f} & {r['50%']:.{d}f} & {r['99%']:.{d}f} & {r['max']:.{d}f} \\\\\n" |
| 77 | +# Stage F (NB-11): the Max column's extreme market-adjusted forward return is named (firm, formation year, holding window, second-source verdict). | |
| 78 | +_mx = df.loc[df.fwd_ret_mktadj.idxmax()]; _hl0 = pd.read_csv(f"{T}/hygiene_log.csv"); _hr = _hl0[(_hl0.kind == "firm_year_fwd") & (_hl0.ticker == _mx.ticker) & (_hl0.fiscal_year == _mx.fiscal_year)] | |
| 79 | +assert len(_hr) == 1, ("maximum forward return without a hygiene verdict", _mx.ticker, _mx.fiscal_year); _hr = _hr.iloc[0] | |
| 80 | +_mxpct = f"{_mx.fwd_ret_mktadj * 100:+,.0f}".replace(",", "{,}") | |
| 81 | +MAXRET_NOTE = (f"The maximum of the market-adjusted forward return ({_mx.fwd_ret_mktadj:.2f}, i.e.\\ {_mxpct}\\%) is {tex(_mx.ticker)} in formation year {int(_mx.fiscal_year)} (holding window {_hr.date.replace('..', ' to ')})" | |
| 82 | + + (", also the maximum of the raw return" if df.fwd_ret.idxmax() == df.fwd_ret_mktadj.idxmax() else "") + f"; its second-source verdict is `{_hr.verdict.replace('_', ' ')}' (FinData OS daily bars, {tex(_hr.note).replace('->', '$\\to$')}). ") | |
| 83 | +MAC["maxMadjRetTicker"] = tex(_mx.ticker); MAC["maxMadjRetYear"] = str(int(_mx.fiscal_year)); MAC["maxMadjRetPct"] = _mxpct | |
| 74 | 84 | table("tab_summary", "tab:summ", "Summary statistics of the estimation sample.", |
| 75 | 85 | f"Firm-years with a valid twelve-month forward return, formation cohorts {YRS} ($N$ = {NOBS} firm-years, {NFIRMS} firms; iShares Russell~3000 holdings of October 2026 plus the S\\&P~500). Semantic scores are section-weighted mean cosine similarities of the firm-year's filing chunks to the anchor concept; returns are simple buy-and-hold fractions.", |
| 76 | 86 | "lrrrrrrrr", ["Variable & $N$ & Mean & SD & Min & P1 & Median & P99 & Max"], body, |
| 77 | 87 | f"Market capitalization = unadjusted FinData OS close $\\times$ point-in-time SEC cover-page shares (log range [{MD['log_cap_min_max_v2'][0]:.1f}, {MD['log_cap_min_max_v2'][1]:.1f}]); {n(META['n_est_without_cap'])} firm-years ({MD['share_est_without_cap_v2'] * 100:.1f}\\%) have no capitalization and enter the equal-weighted sorts only (the committed v2.0 panel had {n(META['n_est_without_cap_v1'])}). " |
| 78 | 88 | f"Return hygiene: {HY['n_flagged_monthly']} monthly returns above 300\\% and {HY['n_flagged_firm_years']} firm-year forward returns above 500\\% were checked against FinData OS daily bars; {HY['verdicts_monthly'].get('series_break', 0)} monthly and {HY['verdicts_firm_year'].get('series_break', 0)} firm-year observations were price splices and are set to missing (Table~\\ref{{tab:hygiene}}), the others are confirmed. " |
| 79 | − f"The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. Source: data/processed\\_r3k\\_v2/analysis\\_panel\\_v2.parquet, results/r3k\\_v2/t1\\_summary\\_stats.csv.", | |
| 89 | + f"The winsorized return is the dependent variable of every regression and of the headline sorts; the raw return is reported beside it. " + MAXRET_NOTE + f"Source: data/processed\\_r3k\\_v2/analysis\\_panel\\_v2.parquet, results/r3k\\_v2/t1\\_summary\\_stats.csv, hygiene\\_log.csv.", | |
| 80 | 90 | size="\\scriptsize", placement="main", sources="results/r3k_v2/t1_summary_stats.csv, panel_meta_v2.json, mktcap_diag.json, hygiene_summary.json") |
| 81 | 91 | # ================================================================ T5 quintile sorts, both universes (main) ==== |
| 82 | 92 | SIGL = [("AI_ADOPT", "AI adoption"), ("AI_INFRA", "AI infrastructure"), ("AI_RISK", "AI disruption risk"), ("AI_COMPOS", "AI composite"), ("AI_PC1", "PC1 of three facets"), ("AI_formw", "Equal-form composite"), ("AI_10k", "10-K composite"), ("SPID_TECH", "SPID projects (legacy quintiles)")] |
@@ -94,11 +104,28 @@ for sname, lab_ in [("r3k", "Panel A: Russell 3000 universe"), ("sp500", "Panel | ||
| 94 | 104 | for i, sfx in enumerate(rows): body += sort_row(SM["sorts"], k, l, sfx, i == 0) |
| 95 | 105 | if sname == "r3k": body += "\\midrule\n" |
| 96 | 106 | VD = R["vw_diag"] |
| 107 | +# Stage F (NB-1): results.json vw_diag mixes two universes -- max_single_firm_weight_v2 / n_cells_above_25pct_v2 are taken over EVERY | |
| 108 | +# full-sample VW sort of the nine signals (r3k_15 regex "_VW$|_VW_w$", raw and winsorized variants counted separately), while | |
| 109 | +# worst_cells_v2 is the composite sort alone. Both statistics are made explicit here from results/r3k_v2/vw_weights_diag.csv (written by | |
| 110 | +# r3k_15 beside results.json), the worst cell of each universe is named, and the all-sorts figures are asserted equal to vw_diag. | |
| 111 | +vwd_ = pd.read_csv(f"{T}/vw_weights_diag.csv"); SIGNAME_VW = {"AI_ADOPT": "AI adoption", "AI_INFRA": "AI infrastructure", "AI_RISK": "AI disruption risk", "AI_COMPOS": "AI composite", "AI_PC1": "PC1", "AI_formw": "equal-form composite", "AI_10k": "10-K composite", "SPID_TECH": "SPID technology", "CYBER": "cybersecurity placebo"} | |
| 112 | +def _vw_universe(suffix_re, prefix_ok=("r3k_", "sp500_")): return vwd_[vwd_["sort"].str.contains(suffix_re, regex=True) & ~vwd_["sort"].str.startswith(prefix_ok)] | |
| 113 | +def _vw_stats(d): | |
| 114 | + w = d.loc[d.max_weight.idxmax()]; base = re.sub(r"_VW(_v1)?(_w)?$", "", w["sort"]); above = d[d.max_weight > 0.25] | |
| 115 | + return {"max": float(w.max_weight), "sort": SIGNAME_VW.get(base, base), "ticker": str(w.max_ticker), "cohort": int(w.cohort), "q": int(w.q), "n_above": int(len(above)), "sorts_above": sorted({SIGNAME_VW.get(re.sub(r"_VW(_v1)?(_w)?$", "", x), x) for x in above["sort"]}), "n_above_distinct": int(above.assign(base=above["sort"].str.replace(r"_w$", "", regex=True)).drop_duplicates(["base", "cohort", "q"]).shape[0])} | |
| 116 | +VWALL = _vw_stats(_vw_universe("_VW$|_VW_w$")); VWCOMP = _vw_stats(vwd_[vwd_["sort"] == "AI_COMPOS_VW"]); VWALL1 = _vw_stats(_vw_universe("_VW_v1$|_VW_v1_w$")); VWCOMP1 = _vw_stats(vwd_[vwd_["sort"] == "AI_COMPOS_VW_v1"]) | |
| 117 | +assert abs(VWALL["max"] - VD["max_single_firm_weight_v2"]) < 1e-6 and VWALL["n_above"] == VD["n_cells_above_25pct_v2"], ("vw_weights_diag.csv and results.json vw_diag disagree", VWALL, VD) | |
| 118 | +assert abs(VWALL1["max"] - VD["max_single_firm_weight_v1"]) < 1e-6 and VWALL1["n_above"] == VD["n_cells_above_25pct_v1"], ("v1 universe", VWALL1, VD) | |
| 119 | +def vwcell(x): return f"{x['ticker']}, {x['cohort']} cohort, quintile {x['q']}" | |
| 120 | +VW_NOTE = (f"VW diagnostic (single-firm weight in a cohort $\\times$ quintile cell): in the composite sort's own cells the maximum is {VWCOMP['max'] * 100:.0f}\\% ({vwcell(VWCOMP)}) and " | |
| 121 | + f"{'no cell exceeds' if VWCOMP['n_above'] == 0 else str(VWCOMP['n_above']) + ' cells exceed'} 25\\%; across all nine full-sample VW sorts of Table~\\ref{{tab:sorts}} the maximum is {VWALL['max'] * 100:.0f}\\% " | |
| 122 | + f"({VWALL['sort']} sort: {vwcell(VWALL)}) and {VWALL['n_above']} entries exceed 25\\% ({VWALL['n_above_distinct']} distinct cells, each counted once for the raw and once for the winsorized return variant); " | |
| 123 | + f"committed v2.0 capitalization: {VD['max_single_firm_weight_v1'] * 100:.1f}\\% and {VD['n_cells_above_25pct_v1']} entries ({VWALL1['n_above_distinct']} distinct cells)") | |
| 97 | 124 | table("tab_sorts_both", "tab:sortsboth", "Quintile sorts on AI language in the Russell~3000 universe and among S\\&P~500 members.", |
| 98 | 125 | f"Mean forward twelve-month return (percent per year) of the within-cohort quintiles of each signal, averaged over the {R['sorts']['AI_COMPOS_EW_w']['n_years']} formation cohorts {YRS}, with the Q5$-$Q1 spread (percentage points), its Newey--West and i.i.d.\\ $t$-statistics and the 95\\% confidence interval over cohorts. EW = equal-weighted; VW = value-weighted on the corrected capitalization. The headline row of each signal is the within-year winsorized return; for the composite the raw and median versions show the sensitivity of the sort to the return tail.", |
| 99 | 126 | "llrrrrrrrrr", HDR5, body, |
| 100 | 127 | TABLE_NOTE_SE + f" Signals are standardized within year and within panel. Economic magnitude: the composite EW wins.\\ spread of {pp(R['samples']['r3k']['sorts']['AI_COMPOS_EW_w']['Q5_minus_Q1'])} pp per year compares with a Q1 mean of {pp(R['samples']['r3k']['sorts']['AI_COMPOS_EW_w']['Q1'])}\\% per year; the top 1\\% of returns contribute {pp(R['sorts']['AI_COMPOS_EW']['top1pct_contribution'])} pp of the raw spread. " |
| 101 | − f"VW diagnostic: maximum single-firm weight in any cohort $\\times$ quintile cell {VD['max_single_firm_weight_v2'] * 100:.0f}\\% (committed v2.0 capitalization: {VD['max_single_firm_weight_v1'] * 100:.1f}\\%); cells above 25\\%: {VD['n_cells_above_25pct_v2']} (v2.0: {VD['n_cells_above_25pct_v1']}); the 10\\%-capped VW composite spread is {pp(R['sorts']['AI_COMPOS_VWcap10_w']['Q5_minus_Q1'])} pp (NW $t$ {tv(R['sorts']['AI_COMPOS_VWcap10_w']['t_stat'])}). " | |
| 128 | + + VW_NOTE + f"; the 10\\%-capped VW composite spread is {pp(R['sorts']['AI_COMPOS_VWcap10_w']['Q5_minus_Q1'])} pp (NW $t$ {tv(R['sorts']['AI_COMPOS_VWcap10_w']['t_stat'])}). " | |
| 102 | 129 | f"Romano--Wolf adjusted $p$ of the composite EW wins.\\ spread within the ten-member family of this table: {dict(zip(R2['mht']['sorts_full_w']['members'], R2['mht']['sorts_full_w']['p_romano_wolf']))['AI_COMPOS_EW']:.2f} (Table~\\ref{{tab:mht}}). All signals and weightings: Table~\\ref{{tab:sorts}}.", |
| 103 | 130 | size="\\scriptsize", colsep="3pt", placement="main", sources="results/r3k_v2/results.json (samples.*.sorts, vw_diag), results2.json (mht)") |
| 104 | 131 | # ================================================================ appendix: full sorts (all signals / weightings, Russell 3000) ==== |
@@ -109,7 +136,7 @@ for k, l in SIGL: | ||
| 109 | 136 | table("tab_sorts", "tab:sorts", "Quintile sorts on every signal and weighting, Russell~3000 universe.", |
| 110 | 137 | f"Companion to Table~\\ref{{tab:sortsboth}}: mean forward twelve-month return (percent per year) by within-cohort quintile for all eight signals, equal-weighted (winsorized, raw, median) and value-weighted on the corrected capitalization (unrestricted and with single-firm weights capped at 10\\%), averaged over the {R['sorts']['AI_COMPOS_EW']['n_years']} cohorts {YRS} ($N$ = {NOBS} firm-years).", |
| 111 | 138 | "llrrrrrrrrr", HDR5, body, |
| 112 | − TABLE_NOTE_SE + f" The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\\ref{{tab:spidbinary}} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\\times$ point-in-time shares); maximum single-firm weight {VD['max_single_firm_weight_v2'] * 100:.0f}\\%.", | |
| 139 | + TABLE_NOTE_SE + f" The SPID legacy quintiles sort a variable whose median and 75th percentile are both zero, so quintiles 1--4 are tie-broken by row order (Table~\\ref{{tab:spidbinary}} reports the binary contrasts and their minimum detectable effects instead). VW weights use the corrected capitalization (unadjusted close $\\times$ point-in-time shares). Maximum single-firm weight in a cohort $\\times$ quintile cell: {VWALL['max'] * 100:.0f}\\% across the nine VW sorts of this table ({VWALL['sort']} sort: {vwcell(VWALL)}; {VWALL['n_above_distinct']} distinct cells above 25\\%, all in the {' and '.join(VWALL['sorts_above'])} sorts) and {VWCOMP['max'] * 100:.0f}\\% in the composite sort ({vwcell(VWCOMP)}; {'no cell' if VWCOMP['n_above'] == 0 else str(VWCOMP['n_above']) + ' cells'} above 25\\%).", | |
| 113 | 140 | size="\\scriptsize", colsep="3pt", placement="appendix", sources="results/r3k_v2/results.json (sorts, vw_diag)") |
| 114 | 141 | # ================================================================ T6 monthly long-short alphas, both universes (main) ==== |
| 115 | 142 | body = "" |
@@ -556,7 +583,8 @@ if RB: | ||
| 556 | 583 | def lr(v): return "$-\\infty$" if v is None or (isinstance(v, float) and np.isinf(v)) else f"{v:.1f}" |
| 557 | 584 | body += ba("log capitalization range (min, max)", f"[{lr(MD['log_cap_min_max_v1'][0])}, {lr(MD['log_cap_min_max_v1'][1])}]", f"[{lr(MD['log_cap_min_max_v2'][0])}, {lr(MD['log_cap_min_max_v2'][1])}]") |
| 558 | 585 | body += ba("AAPL capitalization, June 2011 (USD bn; rank)", f"{MD['AAPL_cohort2010_cap_v1_usd'] / 1e9:.1f} (rank {MD['AAPL_cohort2010_rank_v1']:.0f})", f"{MD['AAPL_cohort2010_cap_v2_usd'] / 1e9:.0f} (rank {MD['AAPL_cohort2010_rank_v2']:.0f})") |
| 559 | − body += ba("Max single-firm VW weight in a cohort $\\times$ quintile; cells $>$ 25\\%", f"{VD['max_single_firm_weight_v1'] * 100:.1f}\\%; {VD['n_cells_above_25pct_v1']}", f"{VD['max_single_firm_weight_v2'] * 100:.0f}\\%; {VD['n_cells_above_25pct_v2']}") | |
| 586 | + body += ba("Max single-firm VW weight in a cohort $\\times$ quintile, all nine VW sorts (raw and wins.\\ variants); entries $>$ 25\\%", f"{VD['max_single_firm_weight_v1'] * 100:.1f}\\% ({VWALL1['ticker']}); {VD['n_cells_above_25pct_v1']}", f"{VD['max_single_firm_weight_v2'] * 100:.0f}\\% ({VWALL['sort']} sort, {vwcell(VWALL)}); {VD['n_cells_above_25pct_v2']}") | |
| 587 | + body += ba("Max single-firm VW weight in a cohort $\\times$ quintile, composite sort; cells $>$ 25\\%", f"{VWCOMP1['max'] * 100:.1f}\\% ({VWCOMP1['ticker']}); {VWCOMP1['n_above']}", f"{VWCOMP['max'] * 100:.0f}\\% ({vwcell(VWCOMP)}); {VWCOMP['n_above']}") | |
| 560 | 588 | body += ba("EW composite Q5$-$Q1, full (pp) [NW $t$]", f"{pp(S0['AI_COMPOS_EW']['Q5_minus_Q1'])} [{tv(S0['AI_COMPOS_EW']['t_stat'])}]", f"{pp(S['AI_COMPOS_EW']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW']['t_stat'])}] $\\rightarrow$ {pp(S['AI_COMPOS_EW_w']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW_w']['t_stat'])}; plain {tv(S['AI_COMPOS_EW_w']['t_plain'])}], CI {ci(S['AI_COMPOS_EW_w']['ci95'])}; median {pp(S['AI_COMPOS_EW_med']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_EW_med']['t_stat'])}]") |
| 561 | 589 | body += ba("VW composite: Q1 / Q5 / Q5$-$Q1 [NW $t$]", f"{pp(S0['AI_COMPOS_VW']['Q1'])} / {pp(S0['AI_COMPOS_VW']['Q5'])} / {pp(S0['AI_COMPOS_VW']['Q5_minus_Q1'])} [{tv(S0['AI_COMPOS_VW']['t_stat'])}]", f"{pp(S['AI_COMPOS_VW']['Q1'])} / {pp(S['AI_COMPOS_VW']['Q5'])} / {pp(S['AI_COMPOS_VW']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_VW']['t_stat'])}] $\\rightarrow$ wins.\\ {pp(S['AI_COMPOS_VW_w']['Q5_minus_Q1'])} [{tv(S['AI_COMPOS_VW_w']['t_stat'])}]") |
| 562 | 590 | body += ba("Fama--MacBeth composite slope (pp/SD) [NW $t$]", f"{FM0['mean_coef'] * 100:+.2f} [{tv(FM0['t'])}]", f"{FM['compos']['z_AI_COMPOS']['mean_coef'] * 100:+.2f} [{tv(FM['compos']['z_AI_COMPOS']['t'])}] $\\rightarrow$ {FM['compos_w']['z_AI_COMPOS']['mean_coef'] * 100:+.2f} [{tv(FM['compos_w']['z_AI_COMPOS']['t'])}; plain {tv(FM['compos_w']['z_AI_COMPOS']['t_plain'])}], CI {ci(FM['compos_w']['z_AI_COMPOS']['ci95'], 2)}") |
@@ -621,7 +649,7 @@ MAC.update({"nObs": n(R["n_obs"]), "nFirms": n(R["n_firms"]), "nFirmYearsPanel": | ||
| 621 | 649 | "sectNeuPost": pp(SN["post"]["mean"]), "sectNeuPostT": tv(SN["post"]["t"]), "sectNeuFull": pp(SN["full"]["mean"]), "sectNeuFullT": tv(SN["full"]["t"]), "sectNeuFullW": pp(SN["full_w"]["mean"]), "sectNeuFullWT": tv(SN["full_w"]["t"]), "residSortFullW": pp(RSd["full_w"]["mean"]), "residSortFullWT": tv(RSd["full_w"]["t"]), "techBetaPost": f"{M['post'].get('tech_beta', float('nan')):.2f}", "techBetaFull": f"{M['full'].get('tech_beta', float('nan')):.2f}", "ffsixTechFull": f1(M["full"].get("ff6_tech_alpha_pct", float("nan"))), "ffsixTechFullT": f"{M['full'].get('ff6_tech_t', float('nan')):.2f}", "sharpeFull": f"{M['full']['sharpe']:.2f}", "meanLSFull": f1(M["full"]["mean_ann_pct"]), "hacFull": f"{M['full']['hac_t']:.2f}", "ffsixTechPost": f1(M["post"].get("ff6_tech_alpha_pct", float("nan"))), "ffsixTechPostT": f"{M['post'].get('ff6_tech_t', float('nan')):.2f}", |
| 622 | 650 | "fmPostSector": f"{Lc_['fm_all_raw_sector_post']['slope_pct']:+.2f}", "fmPostSectorT": tv(Lc_["fm_all_raw_sector_post"]["t_nw"]), "fmPost": f"{Lc_['fm_all_raw_nosector_post']['slope_pct']:+.2f}", "fmPostT": tv(Lc_["fm_all_raw_nosector_post"]["t_nw"]), "syPost": f"{Lc_['pooled_all_post']['sector_year_fe']['coef_pct']:+.2f}", "syPostT": f"{Lc_['pooled_all_post']['sector_year_fe']['t_firm']:.2f}", "syPostTtw": tv(Lc_['pooled_all_post']['sector_year_fe']['t_twoway']), |
| 623 | 651 | "fmFullSector": f"{Lc_['fm_all_raw_sector_full']['slope_pct']:+.2f}", "fmFullSectorT": tv(Lc_["fm_all_raw_sector_full"]["t_nw"]), "fmFullSectorTplain": tv(Lc_["fm_all_raw_sector_full"]["t_plain"]), "fmFullSectorW": f"{Lc_['fm_all_w_sector_full']['slope_pct']:+.2f}", "fmFullSectorWT": tv(Lc_["fm_all_w_sector_full"]["t_nw"]), "fmFullSectorWTplain": tv(Lc_["fm_all_w_sector_full"]["t_plain"]), "fmFullSectorExEnergyW": f"{Lc_['fm_ex_energy_w_sector_full']['slope_pct']:+.2f}", "fmFullSectorExEnergyWT": tv(Lc_["fm_ex_energy_w_sector_full"]["t_nw"]), "fixedBcv": f"{FB:.2f}", "fmPre": f"{Lc_['fm_all_raw_nosector_pre']['slope_pct']:+.2f}", "fmPreT": tv(Lc_["fm_all_raw_nosector_pre"]["t_nw"]), "fmFull": f"{Lc_['fm_all_raw_nosector_full']['slope_pct']:+.2f}", "fmFullT": tv(Lc_["fm_all_raw_nosector_full"]["t_nw"]), "syFull": f"{Lc_['pooled_all_full']['sector_year_fe']['coef_pct']:+.2f}", "syFullT": f"{Lc_['pooled_all_full']['sector_year_fe']['t_firm']:.2f}", "syFullTtw": tv(Lc_['pooled_all_full']['sector_year_fe']['t_twoway']), "syFullPwild": pv(Lc_["pooled_all_full"]["sector_year_fe"].get("p_wild_webb")), |
| 624 | − "placeboRank": str(PB["rank_of_2022"]), "placeboN": str(PB["n_candidates"]), "placeboRankW": str(PB["winsorized"]["rank_of_2022"]), "permP": f"{PB['permutation']['p_one_sided']:.2f}", "permPW": f"{PB['winsorized']['permutation']['p_one_sided']:.2f}", "permRank": str(PB["permutation"]["rank_among_subsets"]), "supWald": f"{PB['sup_wald']['sup_wald']:.2f}", "supWaldP": f"{PB['sup_wald']['p_simulated']:.2f}", "supWaldW": f"{PB['winsorized']['sup_wald']['sup_wald']:.2f}", "supWaldWP": f"{PB['winsorized']['sup_wald']['p_simulated']:.2f}", "placeboBest": max(PB["diff_post_minus_pre_pct_by_break_year"], key=PB["diff_post_minus_pre_pct_by_break_year"].get), "placeboDiffTwentyTwo": f1(PB["diff_post_minus_pre_pct_by_break_year"]["2022"]), | |
| 652 | + "placeboRank": str(PB["rank_of_2022"]), "placeboN": str(PB["n_candidates"]), "placeboRankW": str(PB["winsorized"]["rank_of_2022"]), "placeboRankOrd": ordinal(PB["rank_of_2022"]), "placeboRankWOrd": ordinal(PB["winsorized"]["rank_of_2022"]), "permP": f"{PB['permutation']['p_one_sided']:.2f}", "permPW": f"{PB['winsorized']['permutation']['p_one_sided']:.2f}", "permRank": str(PB["permutation"]["rank_among_subsets"]), "supWald": f"{PB['sup_wald']['sup_wald']:.2f}", "supWaldP": f"{PB['sup_wald']['p_simulated']:.2f}", "supWaldW": f"{PB['winsorized']['sup_wald']['sup_wald']:.2f}", "supWaldWP": f"{PB['winsorized']['sup_wald']['p_simulated']:.2f}", "placeboBest": max(PB["diff_post_minus_pre_pct_by_break_year"], key=PB["diff_post_minus_pre_pct_by_break_year"].get), "placeboDiffTwentyTwo": f1(PB["diff_post_minus_pre_pct_by_break_year"]["2022"]), | |
| 625 | 653 | "didCont": f"{Dd['continuous']['coef'] * 100:+.2f}", "didContT": f"{Dd['continuous']['t']:.2f}", "didBin": f"{Dd['binary']['coef'] * 100:+.2f}", "didBinT": f"{Dd['binary']['t']:.2f}", |
| 626 | 654 | "evtTwentyTwo": f1(ET[2022]["abs_slope"] * 100), "evtTwentyFour": f1(ET[2024]["abs_slope"] * 100), "evtEighteen": f1(ET[2018]["abs_slope"] * 100), "evtDiffTwentyTwo": f1(ET[2022]["diff_from_ref"] * 100), "evtDiffTwentyThree": f1(ET[2023]["diff_from_ref"] * 100), "evtDiffTwentyFour": f1(ET[2024]["diff_from_ref"] * 100), "evtDiffTwentyTwoSE": f"{ET[2022]['diff_se'] * 100:.1f}", "evtBase": f1(R2["did_eventtime"]["base_slope_ref_year"] * 100), "evtTrailTwentyTwo": f1(ETt[2022]["diff_from_ref"] * 100), "evtTrailTwentyFour": f1(ETt[2024]["diff_from_ref"] * 100), |
| 627 | 655 | "tenkPost": pp(Nf["10k"]["post"]["mean"]), "tenkPostT": tv(Nf["10k"]["post"]["t"]), "tenkPre": pp(Nf["10k"]["pre"]["mean"]), "tenqPost": pp(Nf["10q"]["post"]["mean"]), "eightkPost": pp(Nf["8k"]["post"]["mean"]), "corrTenK": f"{Nf['10k']['corr_with_baseline']:.2f}", "tenqPre": pp(Nf["10q"]["pre"]["mean"]), "eightkPre": pp(Nf["8k"]["pre"]["mean"]), "corrEightK": f"{Nf['8k']['corr_with_baseline']:.2f}", "hrTenK": f"{hr0['z_AI_10k']['slope_pct']:+.2f}", "hrTenKT": f"{hr0['z_AI_10k']['t']:.2f}", "hrEightK": f"{hr0['z_AI_8k']['slope_pct']:+.2f}", "hrEightKT": f"{hr0['z_AI_8k']['t']:.2f}", "hrTenQ": f"{hr0['z_AI_10q']['slope_pct']:+.2f}", "hrTenQT": f"{hr0['z_AI_10q']['t']:.2f}", "formWaldP": f"{wd['p']:.2f}", "formWaldPraw": f"{wd0['p']:.2f}", "formwFull": pp(Nf["formw"]["full_w"]["mean"]), "formwFullT": tv(Nf["formw"]["full_w"]["t"]), "cohortTwentyTwoPostShare": f"{c22['share_chunks_post_launch'] * 100:.1f}", "cohortTwentyTwoPreOnly": f1(c22["spread_prelaunch_text_only_w_pct"]), "cohortTwentyTwoFullSame": f1(c22["spread_full_text_same_firms_w_pct"]), |
@@ -629,7 +657,7 @@ MAC.update({"nObs": n(R["n_obs"]), "nFirms": n(R["n_firms"]), "nFirmYearsPanel": | ||
| 629 | 657 | "esgPost": pp(PLm["ESG commitment"]["post"]["mean"]), "climPost": pp(PLm["Climate risk"]["post"]["mean"]), "cyberPost": pp(PLm["Cybersecurity"]["post"]["mean"]), "autoPost": pp(PLm["Automation/labor"]["post"]["mean"]), "cyberPostW": pp(PLm["Cybersecurity"]["post_w"]["mean"]), "cyberIntT": f"{PLm['Cybersecurity']['interaction']['t_firm']:.2f}", "cyberIntTtw": f"{PLm['Cybersecurity']['interaction']['t_twoway']:.2f}", "cyberIntRW": f"{rw['Cybersecurity']:.2f}", "riskIntTtw": f"{PLm['AI disruption risk']['interaction']['t_twoway']:.2f}", "esgIntTtw": f"{PLm['ESG commitment']['interaction']['t_twoway']:.2f}", |
| 630 | 658 | "riskPost": pp(PLm["AI disruption risk"]["post"]["mean"]), "riskPostT": tv(PLm["AI disruption risk"]["post"]["t"]), "riskPre": pp(PLm["AI disruption risk"]["pre"]["mean"]), "adoptPost": pp(PLm["AI adoption"]["post"]["mean"]), "infraPost": pp(PLm["AI infrastructure"]["post"]["mean"]), "macroPost": pp(PLm["Macro risk"]["post"]["mean"]), "riskFFsixPost": f1(R["ls_alpha"]["AI_RISK"]["post"]["ff6_alpha_ann_pct"]), "riskFFsixPostT": tv(R["ls_alpha"]["AI_RISK"]["post"]["ff6_alpha_t"]), "pcaPostW": pp(SP["AI_PC1"]["post_w"]["mean"]), "pcaFullW": pp(SP["AI_PC1"]["full_w"]["mean"]) if "full_w" in SP["AI_PC1"] else pp(S["AI_PC1_EW_w"]["Q5_minus_Q1"]), "pcaFullWT": tv(S["AI_PC1_EW_w"]["t_stat"]), "pcaEVR": f"{R['pca']['evr'][0] * 100:.1f}", "pcaPost": pp(SP["AI_PC1"]["post"]["mean"]), |
| 631 | 659 | "nEntrantFirms": n(EN["n_firms"] - EN["n_seasoned"]), "nSeasoned": n(EN["n_seasoned"]), "rankAuto": f"{cv['rank_autocorr_lag1']:.2f}", "rankAutoFive": f"{cv['rank_autocorr_lag5']:.2f}", "entrantPrem": f1(EN["entrant_minus_seasoned_fwd_pct"]), "seasonedPost": pp(EN["seasoned"]["post"]["mean"]), "seasonedPre": pp(EN["seasoned"]["pre"]["mean"]), "seasonedFullW": pp(EN["seasoned"]["full_w"]["mean"]), "seasonedFullWT": tv(EN["seasoned"]["full_w"]["t"]), "listedPre": pp(EN["listed36"]["pre"]["mean"]), "listedPost": pp(EN["listed36"]["post"]["mean"]), "magSevenPost": pp(M7["ex_mag7_post"]["mean"]), "exTopFiveHundredPost": pp(M7["ex_top500_post"]["mean"]), "exTopFiveHundredPre": pp(M7["ex_top500_pre"]["mean"]), "sixmPost": pp(AH["6m"]["post"]["mean"]), "iwvPost": pp(AB["iwv_adj"]["post"]["mean"]), "iwvPre": pp(AB["iwv_adj"]["pre"]["mean"]), "fvolPost": f1(R2["fwd_vol"]["post"]["mean"] * 100), "fvolPostT": tv(R2["fwd_vol"]["post"]["t"]), "fvolPre": f1(R2["fwd_vol"]["pre"]["mean"] * 100), |
| 632 | − "breakInfraT": tv(B["AI_INFRA"]["interaction_t_firm"]), "breakComposT": tv(B["AI_COMPOS"]["interaction_t_firm"]), "breakComposTtw": tv(B["AI_COMPOS"]["interaction_t_twoway"]), "breakComposPwild": pv(B["AI_COMPOS"].get("interaction_p_wild_webb")), "breakCompos": f"{B['AI_COMPOS']['interaction'] * 100:+.2f}", "vwQone": pp(S["AI_COMPOS_VW"]["Q1"]), "vwQfive": pp(S["AI_COMPOS_VW"]["Q5"]), "ewQone": pp(S["AI_COMPOS_EW"]["Q1"]), "ewQfive": pp(S["AI_COMPOS_EW"]["Q5"]), "vwMaxWeight": f"{R['vw_diag']['max_single_firm_weight_v2'] * 100:.0f}", "vwMaxWeightVone": f"{R['vw_diag']['max_single_firm_weight_v1'] * 100:.1f}", | |
| 660 | + "breakInfraT": tv(B["AI_INFRA"]["interaction_t_firm"]), "breakComposT": tv(B["AI_COMPOS"]["interaction_t_firm"]), "breakComposTtw": tv(B["AI_COMPOS"]["interaction_t_twoway"]), "breakComposPwild": pv(B["AI_COMPOS"].get("interaction_p_wild_webb")), "breakCompos": f"{B['AI_COMPOS']['interaction'] * 100:+.2f}", "vwQone": pp(S["AI_COMPOS_VW"]["Q1"]), "vwQfive": pp(S["AI_COMPOS_VW"]["Q5"]), "ewQone": pp(S["AI_COMPOS_EW"]["Q1"]), "ewQfive": pp(S["AI_COMPOS_EW"]["Q5"]), "vwMaxWeight": f"{R['vw_diag']['max_single_firm_weight_v2'] * 100:.0f}", "vwMaxWeightVone": f"{R['vw_diag']['max_single_firm_weight_v1'] * 100:.1f}", "vwMaxWeightAll": f"{VWALL['max'] * 100:.0f}", "vwMaxWeightAllSort": VWALL["sort"], "vwMaxWeightAllCell": vwcell(VWALL), "vwCellsAboveAll": str(VWALL["n_above"]), "vwCellsAboveAllDistinct": str(VWALL["n_above_distinct"]), "vwSortsAboveAll": " and ".join(VWALL["sorts_above"]), "vwMaxWeightCompos": f"{VWCOMP['max'] * 100:.0f}", "vwMaxWeightComposCell": vwcell(VWCOMP), "vwCellsAboveCompos": str(VWCOMP["n_above"]), "vwMaxWeightComposVone": f"{VWCOMP1['max'] * 100:.1f}", "vwCellsAboveComposVone": str(VWCOMP1["n_above"]), | |
| 633 | 661 | "feCompos": f"{PF['M7_all_spid']['coefs']['z_AI_COMPOS']['coef'] * 100:+.2f}", "feComposT": f"{PF['M7_all_spid']['coefs']['z_AI_COMPOS']['t']:.2f}", "feAdopt": f"{PF['M2_adopt_ctrl']['coefs']['z_AI_ADOPT']['coef'] * 100:+.2f}", "feAdoptT": f"{PF['M2_adopt_ctrl']['coefs']['z_AI_ADOPT']['t']:.2f}", "feInfra": f"{PF['M3_infra_ctrl']['coefs']['z_AI_INFRA']['coef'] * 100:+.2f}", "feInfraT": f"{PF['M3_infra_ctrl']['coefs']['z_AI_INFRA']['t']:.2f}", "feSize": f"{PF['M2_adopt_ctrl']['coefs']['z_size']['coef'] * 100:+.2f}", "feSizeT": f"{PF['M2_adopt_ctrl']['coefs']['z_size']['t']:.2f}", |
| 634 | 662 | "nLargeFirms": n(Gm["large_top1000"]["firms"]), "nSmallFirms": n(Gm["small_rest"]["firms"]), "nSmall": n(Gm["small_rest"]["n"]), "largeFFsixPost": f1(Gm["large_top1000"]["ls"]["post"]["ff6_alpha_ann_pct"]), "largeFFsixPostT": tv(Gm["large_top1000"]["ls"]["post"]["ff6_alpha_t"]), "largeSharpePost": f"{Gm['large_top1000']['ls']['post']['sharpe']:.2f}", "smallSharpePost": f"{Gm['small_rest']['ls']['post']['sharpe']:.2f}", "smallSharpePre": f"{Gm['small_rest']['ls']['pre']['sharpe']:.2f}", "largeSharpePre": f"{Gm['large_top1000']['ls']['pre']['sharpe']:.2f}", "smallFull": pp(Gm["small_rest"]["sort_EW"]["Q5_minus_Q1"]), "smallFullT": tv(Gm["small_rest"]["sort_EW"]["t_stat"]), "largeFull": pp(Gm["large_top1000"]["sort_EW"]["Q5_minus_Q1"]), "largeFullT": tv(Gm["large_top1000"]["sort_EW"]["t_stat"]), "smallFullW": pp(Gm["small_rest"]["sort_EW_w"]["Q5_minus_Q1"]), "smallFullWT": tv(Gm["small_rest"]["sort_EW_w"]["t_stat"]), "largeFullW": pp(Gm["large_top1000"]["sort_EW_w"]["Q5_minus_Q1"]), "largeFullWT": tv(Gm["large_top1000"]["sort_EW_w"]["t_stat"]), "sizeGroupChange": f"{MD['share_firm_years_changing_size_group'] * 100:.1f}", "nNoCap": n(META["n_est_without_cap"]), "nNoCapVone": n(META["n_est_without_cap_v1"]), |
| 635 | 663 | "sizeNeuFull": pp(sn["full"]["Q5_minus_Q1"]), "sizeNeuFullT": tv(sn["full"]["t_stat"]), "sizeNeuPost": pp(sn["post"]["mean"]), "sizeNeuPre": pp(sn["pre"]["mean"]), "sizeNeuFullW": pp(sn["full_w"]["Q5_minus_Q1"]), "sizeNeuFullWT": tv(sn["full_w"]["t_stat"]), "largeCohorts": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["large_top1000"]["post"]["by_cohort"].items()), "smallCohorts": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["small_rest"]["post"]["by_cohort"].items()), "largeCohortsW": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["large_top1000"]["post_w"]["by_cohort"].items()), "smallCohortsW": ", ".join(f"{y}: {v * 100:+.1f}" for y, v in Gm["small_rest"]["post_w"]["by_cohort"].items()), |
modified
scripts/stageE_writing_macros.py
+14 −0
@@ -37,6 +37,17 @@ def num(x: float, d: int = 2) -> str: | ||
| 37 | 37 | return f"{x:.{d}f}" |
| 38 | 38 | |
| 39 | 39 | |
| 40 | +def ordinal(x) -> str: | |
| 41 | + """'4' -> '4th', '21' -> '21st', '63' -> '63rd', '11' -> '11th'; a non-integer value (e.g. a percentile '99.8') takes 'th'. | |
| 42 | + Stage F (NB-4): the suffix is generated with the rank so the two cannot diverge on regeneration.""" | |
| 43 | + sx = str(x) | |
| 44 | + if not sx.lstrip("-").isdigit(): | |
| 45 | + return f"{sx}th" | |
| 46 | + k = int(sx) | |
| 47 | + sfx = "th" if 10 <= k % 100 <= 20 else {1: "st", 2: "nd", 3: "rd"}.get(k % 10, "th") | |
| 48 | + return f"{k}{sfx}" | |
| 49 | + | |
| 50 | + | |
| 40 | 51 | def thousands(n: int) -> str: |
| 41 | 52 | return f"{int(n):,}".replace(",", "{,}") |
| 42 | 53 | |
@@ -121,6 +132,9 @@ M["mIdaRankCore"] = str(int(ranks["IDA"]["rc"])) | ||
| 121 | 132 | M["mHeRank"] = str(int(ranks["HE"]["r"])) |
| 122 | 133 | M["mHeRankStrip"] = str(int(ranks["HE"]["rs"])) |
| 123 | 134 | M["mHeRankCore"] = str(int(ranks["HE"]["rc"])) |
| 135 | +for _k in ("mIdaRank", "mIdaRankStrip", "mIdaRankCore", "mHeRank", "mHeRankStrip", "mHeRankCore", "mUtilSectorRank", | |
| 136 | + "mProbeNegatedPct", "mProbeUtilityPct", "mProbeParaphrasePct", "mProbeCyberPct", "mProbeUnrelatedPct"): | |
| 137 | + M[_k + "Ord"] = ordinal(M[_k]) # NB-4: ordinal forms emitted with the values ("4th", "2nd", "99.8th") | |
| 124 | 138 | M["mTopThirtyUtilOriginal"] = str(alt["top30_utilities_original"]) |
| 125 | 139 | M["mTopThirtyUtilCore"] = str(alt["top30_utilities_core_strip"]) |
| 126 | 140 | M["mTopThirtyOverlapStrip"] = str(alt["top30_overlap_strip"]) |
| 127 | 141 | |