FIRC0002 Stage E (Agent 10): main.tex v2.1 metadata, abstract rewritten to the three weakened pillars (EDIT-03, ECON-06, ADVE-01, ADVE-05), cleveref, threeparttable scaling hook; macros_stageE.tex generator (N1 construct-validity, survivorship bounds, SPID provenance, committed-baseline BEFORE values); references.bib repairs (LITE-02 Eisfeldt JF 2026 names, LITE-08 Wu–Lam authors, Wang et al. journal entity)
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
4 changed files +800 −36
modified
paper/main.tex
+44 −30
@@ -40,7 +40,7 @@ | |||||
| 40 | 40 | ||||
| 41 | % --- Figures --- | 41 | % --- Figures --- | ||
| 42 | \usepackage{graphicx} | 42 | \usepackage{graphicx} | ||
| 43 | −\graphicspath{{figures_r3k/}} | 43 | +\graphicspath{{figures_r3k/}{../figures/r3k_v2/}} | ||
| 44 | \usepackage{adjustbox} | 44 | \usepackage{adjustbox} | ||
| 45 | \usepackage{subcaption} | 45 | \usepackage{subcaption} | ||
| 46 | \usepackage{float} | 46 | \usepackage{float} | ||
@@ -56,8 +56,11 @@ | |||||
| 56 | ]{hyperref} | 56 | ]{hyperref} | ||
| 57 | \hypersetup{ | 57 | \hypersetup{ | ||
| 58 | pdftitle = {Does the Language of Artificial Intelligence Move Markets? A Semantic Analysis of Russell 3000 SEC Filings and the Cross-Section of Stock Returns, 2010-2026}, | 58 | pdftitle = {Does the Language of Artificial Intelligence Move Markets? A Semantic Analysis of Russell 3000 SEC Filings and the Cross-Section of Stock Returns, 2010-2026}, | ||
| 59 | − pdfauthor = {Manel Kammoun, Djerrycharli Tandjambianda, Simon-Pierre Boucher} | 59 | + pdfauthor = {Manel Kammoun, Djerrycharli Tandjambianda, Simon-Pierre Boucher}, | ||
| 60 | + pdfsubject = {UQO/FIRC Working Paper No. 0002, version 2.1 (revised after peer review, November 2026)}, | ||||
| 61 | + pdfkeywords = {textual analysis, sentence embeddings, artificial intelligence, disclosure, cross-section of returns, Russell 3000, firm size, event study, generative AI, G12, G14, G17, M41, O33} | ||||
| 60 | } | 62 | } | ||
| 63 | +\usepackage[capitalise,noabbrev]{cleveref} | ||||
| 61 | 64 | ||||
| 62 | % --- Bibliography --- | 65 | % --- Bibliography --- | ||
| 63 | \usepackage[authoryear,round,semicolon]{natbib} | 66 | \usepackage[authoryear,round,semicolon]{natbib} | ||
@@ -92,6 +95,20 @@ | |||||
| 92 | \sisetup{group-minimum-digits=4, group-separator={,}} | 95 | \sisetup{group-minimum-digits=4, group-separator={,}} | ||
| 93 | \newcommand{\tstat}[1]{{\footnotesize(\textit{t}=#1)}} | 96 | \newcommand{\tstat}[1]{{\footnotesize(\textit{t}=#1)}} | ||
| 94 | \newcommand{\tablelegend}[1]{\par\vspace{0.25em}\begin{minipage}{\linewidth}\footnotesize\textit{Notes:} #1\end{minipage}} | 97 | \newcommand{\tablelegend}[1]{\par\vspace{0.25em}\begin{minipage}{\linewidth}\footnotesize\textit{Notes:} #1\end{minipage}} | ||
| 98 | +% Generated table fragments carry their own \footnotesize notes paragraph after the tabular. | ||||
| 99 | +% Scaling the whole fragment with \adjustbox would put the notes on one scaled line, so every | ||||
| 100 | +% tabular of the document is wrapped, by an etoolbox hook, in adjustbox{max width=\textwidth}: | ||||
| 101 | +% a tabular wider than the text block is shrunk, a narrower one is untouched, and the notes | ||||
| 102 | +% paragraph is typeset at \textwidth. \widetable is kept as a marker for the fragments that | ||||
| 103 | +% needed scaling before the hook existed. | ||||
| 104 | +% The generated fragments wrap each tabular in a threeparttable (which redefines \tabular, so a | ||||
| 105 | +% hook on tabular would be bypassed); the hook is therefore attached to threeparttable. A fragment | ||||
| 106 | +% whose natural width exceeds \textwidth is shrunk with its notes (the exhibits agent is asked to | ||||
| 107 | +% narrow such fragments at generation; the hook keeps the build free of overfull boxes meanwhile). | ||||
| 108 | +\usepackage{etoolbox} | ||||
| 109 | +\BeforeBeginEnvironment{threeparttable}{\begin{adjustbox}{max width=\textwidth}} | ||||
| 110 | +\AfterEndEnvironment{threeparttable}{\end{adjustbox}} | ||||
| 111 | +\newcommand{\widetable}[1]{#1} | ||||
| 95 | 112 | ||||
| 96 | % --- Misc --- | 113 | % --- Misc --- | ||
| 97 | \setlength{\emergencystretch}{3em} | 114 | \setlength{\emergencystretch}{3em} | ||
@@ -103,38 +120,28 @@ | |||||
| 103 | % ============================================================================ | 120 | % ============================================================================ | ||
| 104 | \newcommand{\WPnumber}{0002} | 121 | \newcommand{\WPnumber}{0002} | ||
| 105 | \newcommand{\WPtitle}{Does the Language of Artificial Intelligence Move Markets?} | 122 | \newcommand{\WPtitle}{Does the Language of Artificial Intelligence Move Markets?} | ||
| 123 | +% Stage E note (Agent 10, 2026-11-05): the Chief Editor and two referees (ADVE-13, EDIT-16) read the | ||||
| 124 | +% title verb "Move" as causal while every design in the paper is associational. A title change is an | ||||
| 125 | +% author decision (authorization_requests.md, "Not Rule 6"); a descriptive alternative that keeps the | ||||
| 126 | +% subtitle would be "Is the Language of Artificial Intelligence Priced?". Not imposed here. | ||||
| 106 | \newcommand{\WPsubtitle}{A Semantic Analysis of Russell~3000 SEC Filings and the Cross-Section of Stock Returns, 2010--2026} | 127 | \newcommand{\WPsubtitle}{A Semantic Analysis of Russell~3000 SEC Filings and the Cross-Section of Stock Returns, 2010--2026} | ||
| 107 | −\newcommand{\WPdate}{October 28, 2026} | 128 | +\newcommand{\WPdate}{November 2026} | ||
| 108 | −\newcommand{\WPversion}{2.0 (Russell 3000 extension)} | 129 | +\newcommand{\WPversion}{2.1 (Russell 3000 extension, revised after peer review)} | ||
| 109 | \newcommand{\WPfunding}{Research funded by the FIRC (Fonds institutionnel de recherche et de création), Université du Québec en Outaouais.} | 130 | \newcommand{\WPfunding}{Research funded by the FIRC (Fonds institutionnel de recherche et de création), Université du Québec en Outaouais.} | ||
| 110 | \newcommand{\WPabstract}{% | 131 | \newcommand{\WPabstract}{% | ||
| 111 | \begingroup\singlespacing | 132 | \begingroup\singlespacing | ||
| 112 | −We measure artificial-intelligence (AI) engagement in the SEC filings of some 2,500 U.S.\ listed | 133 | +We measure artificial-intelligence (AI) language in the Form~10-K, 10-Q and 8-K filings of | ||
| 113 | −firms---the current S\&P~500 constituents and the holdings of the iShares Russell~3000 ETF---by | 134 | +\mnFirms{} U.S.\ firms (current S\&P~500 members and iShares Russell~3000 holdings, 2010--2026) | ||
| 114 | −embedding every passage of their Form~10-K, 10-Q and 8-K filings of 2010--2026 with a sentence | 135 | +by sentence-transformer similarity to anchor passages, and relate the filing-year signal to | ||
| 115 | −transformer and scoring it against curated AI anchor concepts (internal adoption, infrastructure | 136 | +look-ahead-free forward returns over fifteen formation cohorts (\mnObs{} firm-years). On | ||
| 116 | −exposure, product-disruption risk). The signal of filing year $Y$ is linked to the forward | 137 | +winsorized returns the equal-weighted top-minus-bottom quintile spread is \mspreadFullW{} | ||
| 117 | −twelve-month return from July of $Y{+}1$, so that it predates the holding period by at least six | 138 | +percentage points per year (95\% CI \mspreadFullWCI) and the Fama--MacBeth slope \mfmComposW{} | ||
| 118 | −months. The panel of \mnObs{} firm-years (\mnFirms{} firms) extends our earlier S\&P~500 study, | 139 | +points per standard deviation (CI \mfmComposWCI): a modest premium, robust in regression form, | ||
| 119 | −which rested on current reports only, to annual and quarterly reports and to the mid- and | 140 | +fragile in sort form, and conditional on survival to October 2026. A post-ChatGPT increase | ||
| 120 | −small-capitalization firms that make up most of the Russell~3000, and it adds a second, | 141 | +appears only among the largest thousand firms and in the 2024 cohort; the large-versus-small | ||
| 121 | −independent measure of AI engagement: the AI, data-center, cloud and automation projects that a | 142 | +contrast is insignificant (\mcontrastLarge{} points, $t=\mcontrastLargeT$) and the event-study | ||
| 122 | −large-language-model reader extracts from the same filings. Three results stand out. First, in the | 143 | +gradient depends on the benchmark. Disclosed AI projects extracted by a language model show no | ||
| 123 | −broad universe AI language carries a modest but persistent cross-sectional premium over the whole | 144 | +detectable premium (minimum detectable effect \mspidAnyFirmFullMDE{} points). | ||
| 124 | −sample: the equal-weighted top-minus-bottom quintile spread is \mspreadFull{} percentage points per | ||||
| 125 | −year ($t=\mspreadFullT$) and the Fama--MacBeth slope \mrkFMcompos{} points per standard deviation | ||||
| 126 | −($t=\mrkFMcomposT$), with a significant within-sector component. Second, the sharp post-ChatGPT | ||||
| 127 | −increase documented for the S\&P~500---which we replicate within the new panel (\mspSpreadPre{} to | ||||
| 128 | −\mspSpreadPost{} points)---does not generalize: in the Russell~3000 the post-launch spread is | ||||
| 129 | −\mspreadPost{} points against \mspreadPre{} before, the 2022 cohort is negative, a placebo over ten | ||||
| 130 | −candidate break years ranks 2022 \mplaceboRank{}th, and the monthly long--short earns no | ||||
| 131 | −post-launch alpha. The increase is confined to the largest thousand firms (\mlargePre{} to | ||||
| 132 | −\mlargePost{} points), whereas the smaller firms earned a premium of about \msmallPre{} points | ||||
| 133 | −before the launch and \msmallPost{} after; the ChatGPT event study shows the same size gradient | ||||
| 134 | −(\mevLarge{} versus \mevSmall{} points of one-year abnormal return). Third, disclosed AI projects | ||||
| 135 | −carry no return premium, while the language of annual reports carries the strongest one. The | ||||
| 136 | −evidence is consistent with a persistent, partly within-industry premium on AI exposure in the | ||||
| 137 | −broad market and with a large-cap re-rating after 2022, not with a market-wide regime change. | ||||
| 138 | \par\endgroup | 145 | \par\endgroup | ||
| 139 | } | 146 | } | ||
| 140 | \newcommand{\WPkeywords}{textual analysis, sentence embeddings, artificial intelligence, disclosure, cross-section of returns, Russell 3000, firm size, event study, generative AI} | 147 | \newcommand{\WPkeywords}{textual analysis, sentence embeddings, artificial intelligence, disclosure, cross-section of returns, Russell 3000, firm size, event study, generative AI} | ||
@@ -160,7 +167,12 @@ broad market and with a large-cap re-rating after 2022, not with a market-wide r | |||||
| 160 | % ============================================================================ | 167 | % ============================================================================ | ||
| 161 | % DOCUMENT | 168 | % DOCUMENT | ||
| 162 | % ============================================================================ | 169 | % ============================================================================ | ||
| 170 | +% Number macros: tables/macros.tex (scripts/r3k_20_tables_v2.py, 442 macros from results/r3k_v2), | ||||
| 171 | +% sections/macros_stageE.tex (scripts/stageE_writing_macros.py: the N1 corpus counts of | ||||
| 172 | +% ../results/n1/corpus_macros.tex re-emitted with letter-only names, N1 construct-validity numbers, | ||||
| 173 | +% survivorship bounds, SPID provenance, and the committed-baseline BEFORE values \mvone...). | ||||
| 163 | \input{tables/macros} | 174 | \input{tables/macros} | ||
| 175 | +\input{sections/macros_stageE} | ||||
| 164 | \begin{document} | 176 | \begin{document} | ||
| 165 | 177 | ||||
| 166 | % --- Title Page --- | 178 | % --- Title Page --- | ||
@@ -182,5 +194,7 @@ broad market and with a large-cap re-rating after 2022, not with a market-wide r | |||||
| 182 | 194 | ||||
| 183 | % --- Appendix --- | 195 | % --- Appendix --- | ||
| 184 | \input{appendix/appendix} | 196 | \input{appendix/appendix} | ||
| 197 | +% Stage E (Agent 9, exhibits): appendix-bound and not-yet-placed exhibits, each behind an \ExhibitOnce guard (generated by scripts/r3k_20_tables_v2.py). | ||||
| 198 | +\input{tables/appendix_exhibits} | ||||
| 185 | 199 | ||||
| 186 | \end{document} | 200 | \end{document} | ||
modified
paper/references.bib
+7 −6
@@ -645,21 +645,22 @@ | |||||
| 645 | } | 645 | } | ||
| 646 | 646 | ||||
| 647 | @techreport{wulam2026genai, | 647 | @techreport{wulam2026genai, | ||
| 648 | − author = {Institute for Monetary and Financial Research, Hong Kong}, | 648 | + author = {Wu, Gabriel Shui Tang and Lam, Stephen Lok Hang}, | ||
| 649 | title = {{Do Firms' Stock Prices Respond to AI-related Discussions? Evidence from Earnings Calls}}, | 649 | title = {{Do Firms' Stock Prices Respond to AI-related Discussions? Evidence from Earnings Calls}}, | ||
| 650 | year = {2026}, | 650 | year = {2026}, | ||
| 651 | doi = {10.2139/ssrn.7482259}, | 651 | doi = {10.2139/ssrn.7482259}, | ||
| 652 | − institution = {Elsevier BV}, | 652 | + institution = {Hong Kong Institute for Monetary and Financial Research (HKIMR) / SSRN}, | ||
| 653 | type = {Working paper}, | 653 | type = {Working paper}, | ||
| 654 | − note = {Elsevier BV, DOI 10.2139/ssrn.7482259} | 654 | + note = {SSRN 7482259; the Crossref record lists the HKIMR as author and names the two authors (Hong Kong Monetary Authority) in its abstract; re-resolved 2026-11-05} | ||
| 655 | } | 655 | } | ||
| 656 | 656 | ||||
| 657 | @article{eisfeldt2026generative, | 657 | @article{eisfeldt2026generative, | ||
| 658 | − author = {EISFELDT, ANDREA L. and SCHUBERT, GREGOR and ZHANG, MIAO BEN and TASKA, BLEDI}, | 658 | + author = {Eisfeldt, Andrea L. and Schubert, Gregor and Zhang, Miao Ben and Taska, Bledi}, | ||
| 659 | title = {{Generative AI and Firm Values}}, | 659 | title = {{Generative AI and Firm Values}}, | ||
| 660 | year = {2026}, | 660 | year = {2026}, | ||
| 661 | doi = {10.1111/jofi.70089}, | 661 | doi = {10.1111/jofi.70089}, | ||
| 662 | − journal = {The Journal of Finance} | 662 | + journal = {The Journal of Finance}, | ||
| 663 | + note = {Published online 8 October 2026, article jofi.70089; Crossref record re-resolved 2026-11-05 (names upper-cased in the record)} | ||||
| 663 | } | 664 | } | ||
| 664 | 665 | ||||
| 665 | @techreport{song2026productexposure, | 666 | @techreport{song2026productexposure, | ||
@@ -687,7 +688,7 @@ | |||||
| 687 | title = {{Talk is cheap, green is hard: The effect of AI-washing on corporate energy transition}}, | 688 | title = {{Talk is cheap, green is hard: The effect of AI-washing on corporate energy transition}}, | ||
| 688 | year = {2026}, | 689 | year = {2026}, | ||
| 689 | doi = {10.1016/j.iref.2026.105756}, | 690 | doi = {10.1016/j.iref.2026.105756}, | ||
| 690 | − journal = {International Review of Economics & Finance}, | 691 | + journal = {International Review of Economics \& Finance}, | ||
| 691 | volume = {111}, | 692 | volume = {111}, | ||
| 692 | pages = {105756} | 693 | pages = {105756} | ||
| 693 | } | 694 | } | ||
added
paper/sections/macros_stageE.tex
+313 −0
@@ -0,0 +1,313 @@ | |||||
| 1 | +% Generated by scripts/stageE_writing_macros.py (Stage E, Agent 10) — do not edit by hand. | ||||
| 2 | +% Sources: results/n1/*, results/r3k_v2/*, results/r3k/* (committed baseline 3cf7560 for the mvone* BEFORE values). | ||||
| 3 | +\newcommand{\mMaxSeq}{256} | ||||
| 4 | +\newcommand{\mTokenizerMax}{512} | ||||
| 5 | +\newcommand{\mTruncShare}{94.8} | ||||
| 6 | +\newcommand{\mTruncShareFull}{100.0} | ||||
| 7 | +\newcommand{\mTruncTokensMedian}{661} | ||||
| 8 | +\newcommand{\mTruncWindowWords}{195} | ||||
| 9 | +\newcommand{\mTruncMassDropped}{62} | ||||
| 10 | +\newcommand{\mTruncHitsBeyond}{61} | ||||
| 11 | +\newcommand{\mTruncHitChunksAllBeyond}{51} | ||||
| 12 | +\newcommand{\mTruncScoreBeyond}{0.18} | ||||
| 13 | +\newcommand{\mTruncScoreInside}{0.34} | ||||
| 14 | +\newcommand{\mTruncScoreBoth}{0.43} | ||||
| 15 | +\newcommand{\mTruncSampleN}{10{,}000} | ||||
| 16 | +\newcommand{\mKwFirmSpearman}{0.24} | ||||
| 17 | +\newcommand{\mKwShareZeroFirmYears}{84} | ||||
| 18 | +\newcommand{\mKwQfiveInKwQfive}{28} | ||||
| 19 | +\newcommand{\mKwUtilSpearman}{0.00} | ||||
| 20 | +\newcommand{\mKwUtilShareZero}{94} | ||||
| 21 | +\newcommand{\mKwUtilMeanZ}{0.52} | ||||
| 22 | +\newcommand{\mKwITMeanZ}{0.87} | ||||
| 23 | +\newcommand{\mKwITSpearman}{0.17} | ||||
| 24 | +\newcommand{\mUtilSectorRank}{2} | ||||
| 25 | +\newcommand{\mKwTopDecileShare}{4.8} | ||||
| 26 | +\newcommand{\mKwTopDecileShareExt}{9.8} | ||||
| 27 | +\newcommand{\mKwBottomDecileShare}{0.08} | ||||
| 28 | +\newcommand{\mKwCorrEarlyMin}{0.02} | ||||
| 29 | +\newcommand{\mKwCorrEarlyMax}{0.07} | ||||
| 30 | +\newcommand{\mKwCorrLateMin}{0.24} | ||||
| 31 | +\newcommand{\mKwCorrLateMax}{0.29} | ||||
| 32 | +\newcommand{\mKwShareZeroTwentyTen}{98} | ||||
| 33 | +\newcommand{\mKwShareZeroTwentyTwentyFour}{45} | ||||
| 34 | +\newcommand{\mStripRho}{0.98} | ||||
| 35 | +\newcommand{\mCoreRho}{0.80} | ||||
| 36 | +\newcommand{\mStripQfiveStays}{88} | ||||
| 37 | +\newcommand{\mProbeAffirm}{0.578} | ||||
| 38 | +\newcommand{\mProbeNegated}{0.396} | ||||
| 39 | +\newcommand{\mProbeDiff}{0.18} | ||||
| 40 | +\newcommand{\mProbePairsShare}{89} | ||||
| 41 | +\newcommand{\mProbeNegatedPct}{99.8} | ||||
| 42 | +\newcommand{\mProbeAffirmPct}{99.99} | ||||
| 43 | +\newcommand{\mProbeUtilityPct}{98.1} | ||||
| 44 | +\newcommand{\mProbeParaphrasePct}{99.8} | ||||
| 45 | +\newcommand{\mProbeCyberPct}{94.0} | ||||
| 46 | +\newcommand{\mProbeUnrelatedPct}{43} | ||||
| 47 | +\newcommand{\mProbeUtilInfra}{0.375} | ||||
| 48 | +\newcommand{\mProbeUtilAdopt}{0.268} | ||||
| 49 | +\newcommand{\mProbeParaphraseAdopt}{0.395} | ||||
| 50 | +\newcommand{\mProbeAutomationAdopt}{0.378} | ||||
| 51 | +\newcommand{\mProbeUnrelatedAdopt}{0.064} | ||||
| 52 | +\newcommand{\mAdoptPninetynine}{0.30} | ||||
| 53 | +\newcommand{\mAdoptPninetyninenine}{0.44} | ||||
| 54 | +\newcommand{\mIdaRank}{4} | ||||
| 55 | +\newcommand{\mIdaRankStrip}{4} | ||||
| 56 | +\newcommand{\mIdaRankCore}{7} | ||||
| 57 | +\newcommand{\mHeRank}{11} | ||||
| 58 | +\newcommand{\mHeRankStrip}{21} | ||||
| 59 | +\newcommand{\mHeRankCore}{63} | ||||
| 60 | +\newcommand{\mTopThirtyUtilOriginal}{3} | ||||
| 61 | +\newcommand{\mTopThirtyUtilCore}{2} | ||||
| 62 | +\newcommand{\mTopThirtyOverlapStrip}{22} | ||||
| 63 | +\newcommand{\mSectorZUtil}{0.52} | ||||
| 64 | +\newcommand{\mSectorZIT}{0.87} | ||||
| 65 | +\newcommand{\mKwRho}{0.13} | ||||
| 66 | +\newcommand{\mKwFullW}{+4.7} | ||||
| 67 | +\newcommand{\mKwFullWT}{2.30} | ||||
| 68 | +\newcommand{\mKwFullWTplain}{1.82} | ||||
| 69 | +\newcommand{\mKwFMW}{+0.61} | ||||
| 70 | +\newcommand{\mKwFMWT}{1.25} | ||||
| 71 | +\newcommand{\mKwPreW}{+3.1} | ||||
| 72 | +\newcommand{\mKwPostW}{+11.1} | ||||
| 73 | +\newcommand{\mKwLogRho}{0.14} | ||||
| 74 | +\newcommand{\mKwLogFullW}{+4.7} | ||||
| 75 | +\newcommand{\mKwLogFullWT}{2.30} | ||||
| 76 | +\newcommand{\mKwLogFullWTplain}{1.82} | ||||
| 77 | +\newcommand{\mKwLogFMW}{+0.81} | ||||
| 78 | +\newcommand{\mKwLogFMWT}{1.95} | ||||
| 79 | +\newcommand{\mKwLogPreW}{+3.1} | ||||
| 80 | +\newcommand{\mKwLogPostW}{+11.1} | ||||
| 81 | +\newcommand{\mStripFullW}{+4.1} | ||||
| 82 | +\newcommand{\mStripFullWT}{2.33} | ||||
| 83 | +\newcommand{\mStripFullWTplain}{2.13} | ||||
| 84 | +\newcommand{\mStripFMW}{+1.79} | ||||
| 85 | +\newcommand{\mStripFMWT}{2.70} | ||||
| 86 | +\newcommand{\mStripPreW}{+4.1} | ||||
| 87 | +\newcommand{\mStripPostW}{+4.0} | ||||
| 88 | +\newcommand{\mCoreFullW}{+4.8} | ||||
| 89 | +\newcommand{\mCoreFullWT}{2.69} | ||||
| 90 | +\newcommand{\mCoreFullWTplain}{1.99} | ||||
| 91 | +\newcommand{\mCoreFMW}{+1.74} | ||||
| 92 | +\newcommand{\mCoreFMWT}{3.02} | ||||
| 93 | +\newcommand{\mCorePreW}{+4.7} | ||||
| 94 | +\newcommand{\mCorePostW}{+5.2} | ||||
| 95 | +\newcommand{\mCyberStripRho}{0.35} | ||||
| 96 | +\newcommand{\mCyberStripFullW}{+1.9} | ||||
| 97 | +\newcommand{\mCyberStripFullWT}{0.97} | ||||
| 98 | +\newcommand{\mCyberStripFullWTplain}{1.12} | ||||
| 99 | +\newcommand{\mCyberStripFMW}{+0.92} | ||||
| 100 | +\newcommand{\mCyberStripFMWT}{1.49} | ||||
| 101 | +\newcommand{\mCyberStripPreW}{+0.1} | ||||
| 102 | +\newcommand{\mCyberStripPostW}{+8.8} | ||||
| 103 | +\newcommand{\mKwShareZero}{83.9} | ||||
| 104 | +\newcommand{\mHrComposVsKw}{+1.85} | ||||
| 105 | +\newcommand{\mHrComposVsKwT}{2.37} | ||||
| 106 | +\newcommand{\mHrKw}{+0.36} | ||||
| 107 | +\newcommand{\mHrKwT}{0.68} | ||||
| 108 | +\newcommand{\mHrComposVsStrip}{+0.15} | ||||
| 109 | +\newcommand{\mHrComposVsStripT}{0.06} | ||||
| 110 | +\newcommand{\mHrStrip}{+1.66} | ||||
| 111 | +\newcommand{\mHrStripT}{0.79} | ||||
| 112 | +\newcommand{\mExUtilFullW}{+4.6} | ||||
| 113 | +\newcommand{\mExUtilFullWT}{2.16} | ||||
| 114 | +\newcommand{\mExUtilN}{25{,}420} | ||||
| 115 | +\newcommand{\mUtilShareQfive}{6.5} | ||||
| 116 | +\newcommand{\mUtilShareAll}{3.2} | ||||
| 117 | +\newcommand{\mITShareQfive}{32} | ||||
| 118 | +\newcommand{\mBoundExitThirty}{8.1} | ||||
| 119 | +\newcommand{\mBoundExitHalf}{5.7} | ||||
| 120 | +\newcommand{\mBoundExitFull}{3.3} | ||||
| 121 | +\newcommand{\mExitSmallMin}{11.0} | ||||
| 122 | +\newcommand{\mExitSmallMax}{16.1} | ||||
| 123 | +\newcommand{\mExitLargeMin}{1.9} | ||||
| 124 | +\newcommand{\mExitLargeMax}{2.8} | ||||
| 125 | +\newcommand{\mExitRatioMin}{5.0} | ||||
| 126 | +\newcommand{\mExitRatioMax}{8.1} | ||||
| 127 | +\newcommand{\mnIWVsnapshotsSinceNineteen}{16} | ||||
| 128 | +\newcommand{\mContigAboveRaw}{3} | ||||
| 129 | +\newcommand{\mContigN}{12} | ||||
| 130 | +\newcommand{\mContigAboveW}{2} | ||||
| 131 | +\newcommand{\mContigBestYear}{2017} | ||||
| 132 | +\newcommand{\mContigBestDiff}{+10.7} | ||||
| 133 | +\newcommand{\mPermSubsets}{455} | ||||
| 134 | +\newcommand{\mSupWaldCVfive}{10.8} | ||||
| 135 | +\newcommand{\mRevWithWithoutTwelve}{-1.39} | ||||
| 136 | +\newcommand{\mRevWithWithoutTwelveT}{-0.91} | ||||
| 137 | +\newcommand{\mRevWithWithoutTwentyFour}{+1.35} | ||||
| 138 | +\newcommand{\mRevWithWithoutTwentyFourT}{0.62} | ||||
| 139 | +\newcommand{\mRevNhighWithProjects}{1{,}288} | ||||
| 140 | +\newcommand{\mRevNhigh}{5{,}001} | ||||
| 141 | +\newcommand{\mRevComposThirteenTwentyFourW}{+5.4} | ||||
| 142 | +\newcommand{\mRevComposThirteenTwentyFourWT}{3.13} | ||||
| 143 | +\newcommand{\mEvtRefSlope}{+5.9} | ||||
| 144 | +\newcommand{\mEvtRefT}{5.95} | ||||
| 145 | +\newcommand{\mEvtDiffTwentyTwoT}{-4.49} | ||||
| 146 | +\newcommand{\mEvtDiffTwentyT}{-5.65} | ||||
| 147 | +\newcommand{\mEvtDiffTwentyEighteen}{+1.8} | ||||
| 148 | +\newcommand{\mEvtDiffTwenty}{-6.8} | ||||
| 149 | +\newcommand{\mEvtN}{21{,}111} | ||||
| 150 | +\newcommand{\mAaplCapVone}{9.3} | ||||
| 151 | +\newcommand{\mAaplRankVone}{166} | ||||
| 152 | +\newcommand{\mAaplCapVtwo}{310} | ||||
| 153 | +\newcommand{\mAaplRankVtwo}{2} | ||||
| 154 | +\newcommand{\mLogCapMaxVone}{37.4} | ||||
| 155 | +\newcommand{\mAepCapVoneExp}{16} | ||||
| 156 | +\newcommand{\mAepCapVoneMant}{1.0} | ||||
| 157 | +\newcommand{\mAepCapVtwo}{18.1} | ||||
| 158 | +\newcommand{\mnCapsGtThreeTrillionVone}{29} | ||||
| 159 | +\newcommand{\mnCapsGtThreeTrillionVtwo}{7} | ||||
| 160 | +\newcommand{\mShareNoCapVone}{7.7} | ||||
| 161 | +\newcommand{\mShareNoCapVtwo}{1.1} | ||||
| 162 | +\newcommand{\mnSizeGroupChange}{1{,}915} | ||||
| 163 | +\newcommand{\mvoneNobs}{27{,}468} | ||||
| 164 | +\newcommand{\mvoneNfirms}{2{,}379} | ||||
| 165 | +\newcommand{\mvoneSpreadFull}{+5.5} | ||||
| 166 | +\newcommand{\mvoneSpreadFullT}{2.41} | ||||
| 167 | +\newcommand{\mvoneSpreadFullVW}{+13.9} | ||||
| 168 | +\newcommand{\mvoneSpreadFullVWT}{2.59} | ||||
| 169 | +\newcommand{\mvoneVWQone}{+0.4} | ||||
| 170 | +\newcommand{\mvoneVWQfive}{+14.3} | ||||
| 171 | +\newcommand{\mvoneFMcompos}{+3.07} | ||||
| 172 | +\newcommand{\mvoneFMcomposT}{2.80} | ||||
| 173 | +\newcommand{\mvoneFMsector}{+1.46} | ||||
| 174 | +\newcommand{\mvoneFMsectorT}{4.38} | ||||
| 175 | +\newcommand{\mvoneSectorNeutralFull}{-0.3} | ||||
| 176 | +\newcommand{\mvoneSectorNeutralFullT}{-0.11} | ||||
| 177 | +\newcommand{\mvoneSpreadPre}{+4.9} | ||||
| 178 | +\newcommand{\mvoneSpreadPost}{+7.9} | ||||
| 179 | +\newcommand{\mvonePooledPost}{+8.2} | ||||
| 180 | +\newcommand{\mvonePooledPostT}{1.80} | ||||
| 181 | +\newcommand{\mvonePooledPostW}{+3.0} | ||||
| 182 | +\newcommand{\mvonePooledPostExTwentyFour}{+1.8} | ||||
| 183 | +\newcommand{\mvonePooledPostExTwentyFourT}{0.33} | ||||
| 184 | +\newcommand{\mvoneCohortTwentyTwo}{-8.4} | ||||
| 185 | +\newcommand{\mvoneEntrantPrem}{+11.0} | ||||
| 186 | +\newcommand{\mvoneLargePre}{+2.9} | ||||
| 187 | +\newcommand{\mvoneLargePost}{+12.8} | ||||
| 188 | +\newcommand{\mvoneSmallPre}{+9.6} | ||||
| 189 | +\newcommand{\mvoneSmallPost}{+8.4} | ||||
| 190 | +\newcommand{\mvoneEvFullHL}{+14.0} | ||||
| 191 | +\newcommand{\mvoneEvFullWelch}{3.94} | ||||
| 192 | +\newcommand{\mvoneEvLargeHL}{+19.5} | ||||
| 193 | +\newcommand{\mvoneEvLargeWelch}{5.09} | ||||
| 194 | +\newcommand{\mvoneEvSmallHL}{+9.9} | ||||
| 195 | +\newcommand{\mvoneEvSmallWelch}{1.55} | ||||
| 196 | +\newcommand{\mvoneEvSixtyHL}{+0.9} | ||||
| 197 | +\newcommand{\mvoneEvN}{2{,}226} | ||||
| 198 | +\newcommand{\mvoneSpidT}{0.00} | ||||
| 199 | +\newcommand{\mvoneSpidPostCoefExp}{12} | ||||
| 200 | +\newcommand{\mvoneDidCont}{+1.11} | ||||
| 201 | +\newcommand{\mvoneDidContT}{0.69} | ||||
| 202 | +\newcommand{\mvoneDidBin}{+2.54} | ||||
| 203 | +\newcommand{\mvoneDidBinT}{0.98} | ||||
| 204 | +\newcommand{\mvoneEvtTwentyOne}{+7.0} | ||||
| 205 | +\newcommand{\mvoneEvtTwentyOneSE}{1.1} | ||||
| 206 | +\newcommand{\mvoneEvtTwentyTwo}{+0.0} | ||||
| 207 | +\newcommand{\mvoneEvtTwentyFour}{+5.0} | ||||
| 208 | +\newcommand{\mvoneSpreadPostTnaive}{1.34} | ||||
| 209 | +\newcommand{\mvoneSpidAnyPost}{+6.6} | ||||
| 210 | +\newcommand{\mvoneSpidAnyPostT}{1.68} | ||||
| 211 | +\newcommand{\mvoneSpidPre}{-1.9} | ||||
| 212 | +\newcommand{\mvoneSpidPreT}{-2.64} | ||||
| 213 | +\newcommand{\mvoneSpidLSmean}{-5.28} | ||||
| 214 | +\newcommand{\mvonePlaceboRank}{8} | ||||
| 215 | +\newcommand{\mSpidModel}{gpt-4o-mini} | ||||
| 216 | +\newcommand{\mSpidSampleN}{200} | ||||
| 217 | +\newcommand{\mSpidUniverseMentions}{203{,}944} | ||||
| 218 | +\newcommand{\mSpidEvidenceShare}{59.5} | ||||
| 219 | +\newcommand{\mSpidConfMedian}{0.9} | ||||
| 220 | +\newcommand{\mSpidDbDate}{2026-10-28} | ||||
| 221 | +\newcommand{\mSpidStrata}{63} | ||||
| 222 | +\newcommand{\mHygieneConfirmedMonthly}{37} | ||||
| 223 | +\newcommand{\mHygieneConfirmedFY}{101} | ||||
| 224 | +\newcommand{\mriskFullW}{+7.3} | ||||
| 225 | +\newcommand{\mriskFullWT}{2.20} | ||||
| 226 | +\newcommand{\mriskFullWTplain}{1.88} | ||||
| 227 | +\newcommand{\mriskFull}{+12.3} | ||||
| 228 | +\newcommand{\mriskFullT}{2.99} | ||||
| 229 | +\newcommand{\mriskTopOnePct}{+8.5} | ||||
| 230 | +\newcommand{\mtenkFullW}{+6.2} | ||||
| 231 | +\newcommand{\mtenkFullWT}{1.91} | ||||
| 232 | +\newcommand{\mformwFullWsort}{+5.9} | ||||
| 233 | +\newcommand{\mformwFullWsortT}{1.93} | ||||
| 234 | +\newcommand{\mcyberFullW}{+1.9} | ||||
| 235 | +\newcommand{\mcyberFullWT}{1.02} | ||||
| 236 | +\newcommand{\mcomposTopOnePctW}{+0.9} | ||||
| 237 | +\newcommand{\mtenkPreWsp}{+4.9} | ||||
| 238 | +\newcommand{\mtenkPreWspT}{1.36} | ||||
| 239 | +\newcommand{\mtenkPostWsp}{+11.6} | ||||
| 240 | +\newcommand{\mtenkPostWspT}{2.06} | ||||
| 241 | +\newcommand{\mriskPreWsp}{+5.0} | ||||
| 242 | +\newcommand{\mriskPreWspT}{2.01} | ||||
| 243 | +\newcommand{\mriskPostWsp}{+16.2} | ||||
| 244 | +\newcommand{\mriskPostWspT}{1.74} | ||||
| 245 | +\newcommand{\mcyberPreWsp}{+0.2} | ||||
| 246 | +\newcommand{\mcyberPreWspT}{0.10} | ||||
| 247 | +\newcommand{\mcyberPostWsp}{+8.9} | ||||
| 248 | +\newcommand{\mcyberPostWspT}{5.11} | ||||
| 249 | +\newcommand{\mformwPreWsp}{+4.3} | ||||
| 250 | +\newcommand{\mformwPreWspT}{1.40} | ||||
| 251 | +\newcommand{\mformwPostWsp}{+12.2} | ||||
| 252 | +\newcommand{\mformwPostWspT}{1.85} | ||||
| 253 | +\newcommand{\mpcaPreWsp}{+3.8} | ||||
| 254 | +\newcommand{\mpcaPreWspT}{1.61} | ||||
| 255 | +\newcommand{\mpcaPostWsp}{+8.6} | ||||
| 256 | +\newcommand{\mpcaPostWspT}{1.24} | ||||
| 257 | +\newcommand{\mlargeFortyTwoN}{11{,}356} | ||||
| 258 | +\newcommand{\mlargeFortyTwoFirms}{1{,}280} | ||||
| 259 | +\newcommand{\mlargeFortyTwoPreW}{+4.2} | ||||
| 260 | +\newcommand{\mlargeFortyTwoPreWT}{1.63} | ||||
| 261 | +\newcommand{\mlargeFortyTwoPostW}{+12.8} | ||||
| 262 | +\newcommand{\mlargeFortyTwoPostWT}{2.26} | ||||
| 263 | +\newcommand{\mlargeFortyTwoPostExTwentyFourW}{+3.6} | ||||
| 264 | +\newcommand{\msmallFiftyEightN}{15{,}690} | ||||
| 265 | +\newcommand{\msmallFiftyEightFirms}{1{,}766} | ||||
| 266 | +\newcommand{\msmallFiftyEightPreW}{+4.3} | ||||
| 267 | +\newcommand{\msmallFiftyEightPreWT}{1.28} | ||||
| 268 | +\newcommand{\msmallFiftyEightPostW}{+3.6} | ||||
| 269 | +\newcommand{\msmallFiftyEightPostWT}{0.61} | ||||
| 270 | +\newcommand{\msmallFiftyEightPostExTwentyFourW}{-3.3} | ||||
| 271 | +\newcommand{\mhrTenKW}{+2.78} | ||||
| 272 | +\newcommand{\mhrTenKWT}{2.23} | ||||
| 273 | +\newcommand{\mhrTenQW}{-0.32} | ||||
| 274 | +\newcommand{\mhrTenQWT}{-0.87} | ||||
| 275 | +\newcommand{\mhrEightKW}{+0.55} | ||||
| 276 | +\newcommand{\mhrEightKWT}{1.29} | ||||
| 277 | +\newcommand{\mseasonedPreW}{+3.2} | ||||
| 278 | +\newcommand{\mseasonedPostW}{+8.3} | ||||
| 279 | +\newcommand{\mentrantPremW}{+5.9} | ||||
| 280 | +\newcommand{\mentrantPremRaw}{+9.0} | ||||
| 281 | +\newcommand{\mnObsFirstCohort}{1{,}342} | ||||
| 282 | +\newcommand{\mnObsLastCohort}{2{,}374} | ||||
| 283 | +\newcommand{\mattritionSmallNineteen}{61} | ||||
| 284 | +\newcommand{\mattritionLargeNineteen}{16} | ||||
| 285 | +\newcommand{\mattritionSmallTwentyFour}{12} | ||||
| 286 | +\newcommand{\mattritionLargeTwentyFour}{2} | ||||
| 287 | +\newcommand{\mcohortTwentyOneW}{+12.1} | ||||
| 288 | +\newcommand{\mbreakTwentyOnePreW}{+2.6} | ||||
| 289 | +\newcommand{\mbreakTwentyOnePreWT}{1.13} | ||||
| 290 | +\newcommand{\mbreakTwentyOnePostW}{+7.5} | ||||
| 291 | +\newcommand{\mbreakTwentyOnePostWT}{2.40} | ||||
| 292 | +\newcommand{\mbreakTwentyOneDiffW}{+4.8} | ||||
| 293 | +\newcommand{\mbreakTwentyOneDiffWT}{1.91} | ||||
| 294 | +\newcommand{\mbreakTwentyOneDiffWTtw}{0.88} | ||||
| 295 | +\newcommand{\mContigBestBlock}{2017--2019} | ||||
| 296 | +\newcommand{\mnChunksCorpus}{10{,}720{,}890} | ||||
| 297 | +\newcommand{\mnSectionsCorpus}{1{,}342{,}283} | ||||
| 298 | +\newcommand{\mnFilingsCorpusN}{527{,}548} | ||||
| 299 | +\newcommand{\mnIssuersCorpusN}{2{,}499} | ||||
| 300 | +\newcommand{\mnEmbeddingsCorpus}{10{,}721{,}045} | ||||
| 301 | +\newcommand{\mnChunkScoresCorpus}{10{,}720{,}890} | ||||
| 302 | +\newcommand{\mChunkWordsMedian}{512} | ||||
| 303 | +\newcommand{\mChunkWordsMean}{480} | ||||
| 304 | +\newcommand{\mChunkShareFull}{84.2} | ||||
| 305 | +\newcommand{\mnFilingsTenK}{31{,}775} | ||||
| 306 | +\newcommand{\mnParsedTenK}{31{,}772} | ||||
| 307 | +\newcommand{\mnChunksTenK}{4{,}380{,}094} | ||||
| 308 | +\newcommand{\mnFilingsTenQ}{95{,}167} | ||||
| 309 | +\newcommand{\mnParsedTenQ}{95{,}155} | ||||
| 310 | +\newcommand{\mnChunksTenQ}{5{,}492{,}676} | ||||
| 311 | +\newcommand{\mnFilingsEightK}{400{,}606} | ||||
| 312 | +\newcommand{\mnParsedEightK}{400{,}605} | ||||
| 313 | +\newcommand{\mnChunksEightK}{848{,}120} | ||||
added
scripts/stageE_writing_macros.py
+436 −0
@@ -0,0 +1,436 @@ | |||||
| 1 | +#!/usr/bin/env python3 | ||||
| 2 | +"""Stage E (Agent 10, writing) — supplementary number macros for the manuscript prose. | ||||
| 3 | + | ||||
| 4 | +Every macro below is read from a result file produced by a Stage D / N1 script; nothing is typed. | ||||
| 5 | +Sources: results/n1/tokenizer_audit.json, keyword_baseline.json, keyword_chunk_deciles.csv, | ||||
| 6 | +keyword_corr_by_year.csv, alt_anchor_results.json; results/r3k_v2/results_construct.json, | ||||
| 7 | +results_surv.json, results2.json, mktcap_diag.json, validation/spid_sample_meta.json; | ||||
| 8 | +the committed baseline results/r3k/*.json (HEAD 3cf7560) for the BEFORE values. | ||||
| 9 | +Output: paper/sections/macros_stageE.tex (\\input from paper/main.tex after tables/macros.tex). | ||||
| 10 | +Usage: python3 -I scripts/stageE_writing_macros.py (run from the repository root) | ||||
| 11 | +""" | ||||
| 12 | +from __future__ import annotations | ||||
| 13 | + | ||||
| 14 | +import csv | ||||
| 15 | +import json | ||||
| 16 | +import math | ||||
| 17 | +from pathlib import Path | ||||
| 18 | + | ||||
| 19 | +ROOT = Path(__file__).resolve().parents[1] | ||||
| 20 | +OUT = ROOT / "paper" / "sections" / "macros_stageE.tex" | ||||
| 21 | + | ||||
| 22 | + | ||||
| 23 | +def load(p: str): | ||||
| 24 | + with open(ROOT / p) as f: | ||||
| 25 | + return json.load(f) | ||||
| 26 | + | ||||
| 27 | + | ||||
| 28 | +def pct(x: float, d: int = 1) -> str: | ||||
| 29 | + return f"{100 * x:.{d}f}" | ||||
| 30 | + | ||||
| 31 | + | ||||
| 32 | +def signed(x: float, d: int = 1) -> str: | ||||
| 33 | + return f"{x:+.{d}f}" | ||||
| 34 | + | ||||
| 35 | + | ||||
| 36 | +def num(x: float, d: int = 2) -> str: | ||||
| 37 | + return f"{x:.{d}f}" | ||||
| 38 | + | ||||
| 39 | + | ||||
| 40 | +def thousands(n: int) -> str: | ||||
| 41 | + return f"{int(n):,}".replace(",", "{,}") | ||||
| 42 | + | ||||
| 43 | + | ||||
| 44 | +M: dict[str, str] = {} | ||||
| 45 | + | ||||
| 46 | +# ---------------------------------------------------------------- tokenizer / truncation (N1) | ||||
| 47 | +tok = load("results/n1/tokenizer_audit.json") | ||||
| 48 | +R, K = tok["frames"]["R"], tok["frames"]["K"] | ||||
| 49 | +M["mMaxSeq"] = str(tok["max_seq_length"]) | ||||
| 50 | +M["mTokenizerMax"] = str(tok["model_max_length_tokenizer"]) | ||||
| 51 | +M["mTruncShare"] = pct(R["share_truncated"]) | ||||
| 52 | +M["mTruncShareFull"] = pct(R["share_truncated_full_512w"]) | ||||
| 53 | +M["mTruncTokensMedian"] = str(int(R["tokens_median"])) | ||||
| 54 | +M["mTruncWindowWords"] = str(int(R["words_in_window_median"])) | ||||
| 55 | +M["mTruncMassDropped"] = pct(R["share_of_token_mass_dropped"], 0) | ||||
| 56 | +M["mTruncHitsBeyond"] = pct(K["share_hits_beyond_window"], 0) | ||||
| 57 | +M["mTruncHitChunksAllBeyond"] = pct(K["share_hit_chunks_all_hits_beyond"], 0) | ||||
| 58 | +M["mTruncScoreBeyond"] = num(K["mean_score_adopt_by_hit_class"]["beyond_only"], 2) | ||||
| 59 | +M["mTruncScoreInside"] = num(K["mean_score_adopt_by_hit_class"]["inside_only"], 2) | ||||
| 60 | +M["mTruncScoreBoth"] = num(K["mean_score_adopt_by_hit_class"]["both"], 2) | ||||
| 61 | +M["mTruncSampleN"] = thousands(R["n"]) | ||||
| 62 | + | ||||
| 63 | +# ---------------------------------------------------------------- keyword baseline (N1) | ||||
| 64 | +kw = load("results/n1/keyword_baseline.json") | ||||
| 65 | +M["mKwFirmSpearman"] = num(kw["firm_year"]["adopt_vs_kw_core_wmean"]["spearman"], 2) | ||||
| 66 | +M["mKwShareZeroFirmYears"] = pct(kw["firm_year"]["adopt_vs_kw_core_wmean"]["share_zero_b"], 0) | ||||
| 67 | +M["mKwQfiveInKwQfive"] = pct(kw["firm_year"]["adopt_vs_kw_core_wmean"]["share_a_Q5_in_b_Q5"], 0) | ||||
| 68 | +sec = {r["sector"]: r for r in kw["by_sector"]} | ||||
| 69 | +M["mKwUtilSpearman"] = num(abs(sec["Utilities"]["spearman"]), 2) | ||||
| 70 | +M["mKwUtilShareZero"] = pct(sec["Utilities"]["share_zero_kw"], 0) | ||||
| 71 | +M["mKwUtilMeanZ"] = num(sec["Utilities"]["mean_z_compos"], 2) | ||||
| 72 | +M["mKwITMeanZ"] = num(sec["Information Technology"]["mean_z_compos"], 2) | ||||
| 73 | +M["mKwITSpearman"] = num(sec["Information Technology"]["spearman"], 2) | ||||
| 74 | +rank_by_z = sorted(kw["by_sector"], key=lambda r: -r["mean_z_compos"]) | ||||
| 75 | +M["mUtilSectorRank"] = str([r["sector"] for r in rank_by_z].index("Utilities") + 1) | ||||
| 76 | +with open(ROOT / "results/n1/keyword_chunk_deciles.csv") as f: | ||||
| 77 | + dec = {int(r["decile"]): r for r in csv.DictReader(f)} | ||||
| 78 | +M["mKwTopDecileShare"] = pct(float(dec[10]["share_core_hit"])) | ||||
| 79 | +M["mKwTopDecileShareExt"] = pct(float(dec[10]["share_ext_hit"])) | ||||
| 80 | +M["mKwBottomDecileShare"] = pct(float(dec[1]["share_core_hit"]), 2) | ||||
| 81 | +with open(ROOT / "results/n1/keyword_corr_by_year.csv") as f: | ||||
| 82 | + rows = list(csv.DictReader(f)) | ||||
| 83 | +early = [float(r["spearman_compos_kw"]) for r in rows if int(r["fiscal_year"]) <= 2016] | ||||
| 84 | +late = [float(r["spearman_compos_kw"]) for r in rows if int(r["fiscal_year"]) >= 2022] | ||||
| 85 | +M["mKwCorrEarlyMin"] = num(min(early), 2) | ||||
| 86 | +M["mKwCorrEarlyMax"] = num(max(early), 2) | ||||
| 87 | +M["mKwCorrLateMin"] = num(min(late), 2) | ||||
| 88 | +M["mKwCorrLateMax"] = num(max(late), 2) | ||||
| 89 | +M["mKwShareZeroTwentyTen"] = pct(float(rows[0]["share_zero_kw"]), 0) | ||||
| 90 | +M["mKwShareZeroTwentyTwentyFour"] = pct(float(rows[-1]["share_zero_kw"]), 0) | ||||
| 91 | + | ||||
| 92 | +# ---------------------------------------------------------------- alternative anchors, negation probe (N1, AR-2) | ||||
| 93 | +alt = load("results/n1/alt_anchor_results.json") | ||||
| 94 | +M["mStripRho"] = num(alt["firm_level_spearman_compos_vs_strip"], 3) | ||||
| 95 | +M["mCoreRho"] = num(alt["firm_level_spearman_compos_vs_core_strip"], 2) | ||||
| 96 | +M["mStripQfiveStays"] = pct(alt["firm_year"]["adopt_vs_adopt_strip"]["share_Q5_stays_Q5"], 0) | ||||
| 97 | +pn = alt["probe_negation"]["s_ai_adoption"] | ||||
| 98 | +M["mProbeAffirm"] = num(pn["mean_affirmative"], 3) | ||||
| 99 | +M["mProbeNegated"] = num(pn["mean_negated"], 3) | ||||
| 100 | +M["mProbeDiff"] = num(pn["diff"], 2) | ||||
| 101 | +M["mProbePairsShare"] = pct(pn["share_pairs_aff_gt_neg"], 0) | ||||
| 102 | +M["mProbeNegatedPct"] = pct(alt["probe_pct_rank_in_corpus"]["negated_ai"]) | ||||
| 103 | +M["mProbeAffirmPct"] = pct(alt["probe_pct_rank_in_corpus"]["affirmative_ai"], 2) | ||||
| 104 | +M["mProbeUtilityPct"] = pct(alt["probe_pct_rank_in_corpus"]["utility_data_center_load"]) | ||||
| 105 | +M["mProbeParaphrasePct"] = pct(alt["probe_pct_rank_in_corpus"]["paraphrase_no_keyword"]) | ||||
| 106 | +M["mProbeCyberPct"] = pct(alt["probe_pct_rank_in_corpus"]["cyber_no_ai"]) | ||||
| 107 | +M["mProbeUnrelatedPct"] = pct(alt["probe_pct_rank_in_corpus"]["unrelated"], 0) | ||||
| 108 | +gm = alt["probe_group_means"] | ||||
| 109 | +M["mProbeUtilInfra"] = num(gm["utility_data_center_load"]["s_ai_infrastructure"], 3) | ||||
| 110 | +M["mProbeUtilAdopt"] = num(gm["utility_data_center_load"]["s_ai_adoption"], 3) | ||||
| 111 | +M["mProbeParaphraseAdopt"] = num(gm["paraphrase_no_keyword"]["s_ai_adoption"], 3) | ||||
| 112 | +M["mProbeAutomationAdopt"] = num(gm["automation_no_ai"]["s_ai_adoption"], 3) | ||||
| 113 | +M["mProbeUnrelatedAdopt"] = num(gm["unrelated"]["s_ai_adoption"], 3) | ||||
| 114 | +q = alt["corpus_adopt_quantiles_50_90_95_99_999"] | ||||
| 115 | +M["mAdoptPninetynine"] = num(q[3], 2) | ||||
| 116 | +M["mAdoptPninetyninenine"] = num(q[4], 2) | ||||
| 117 | +ranks = {r["ticker"]: r for r in alt["ida_he_ranks"]} | ||||
| 118 | +M["mIdaRank"] = str(int(ranks["IDA"]["r"])) | ||||
| 119 | +M["mIdaRankStrip"] = str(int(ranks["IDA"]["rs"])) | ||||
| 120 | +M["mIdaRankCore"] = str(int(ranks["IDA"]["rc"])) | ||||
| 121 | +M["mHeRank"] = str(int(ranks["HE"]["r"])) | ||||
| 122 | +M["mHeRankStrip"] = str(int(ranks["HE"]["rs"])) | ||||
| 123 | +M["mHeRankCore"] = str(int(ranks["HE"]["rc"])) | ||||
| 124 | +M["mTopThirtyUtilOriginal"] = str(alt["top30_utilities_original"]) | ||||
| 125 | +M["mTopThirtyUtilCore"] = str(alt["top30_utilities_core_strip"]) | ||||
| 126 | +M["mTopThirtyOverlapStrip"] = str(alt["top30_overlap_strip"]) | ||||
| 127 | +M["mSectorZUtil"] = num(alt["sector_mean_z"]["Utilities"]["z_compos"], 2) | ||||
| 128 | +M["mSectorZIT"] = num(alt["sector_mean_z"]["Information Technology"]["z_compos"], 2) | ||||
| 129 | + | ||||
| 130 | +# ---------------------------------------------------------------- construct validity rows (r3k_23) | ||||
| 131 | +con = load("results/r3k_v2/results_construct.json") | ||||
| 132 | +S = con["signals"] | ||||
| 133 | + | ||||
| 134 | + | ||||
| 135 | +def sig(key: str, prefix: str) -> None: | ||||
| 136 | + s = S[key] | ||||
| 137 | + if s.get("spearman_with_composite") is not None: | ||||
| 138 | + M[prefix + "Rho"] = num(s["spearman_with_composite"], 2) | ||||
| 139 | + M[prefix + "FullW"] = signed(100 * s["full_w"]["mean"]) | ||||
| 140 | + M[prefix + "FullWT"] = num(s["full_w"]["t"]) | ||||
| 141 | + M[prefix + "FullWTplain"] = num(s["full_w"]["t_plain"]) | ||||
| 142 | + M[prefix + "FMW"] = signed(s["fm_w"]["slope_pct"], 2) | ||||
| 143 | + M[prefix + "FMWT"] = num(s["fm_w"]["t"]) | ||||
| 144 | + M[prefix + "PreW"] = signed(100 * s["pre_w"]["mean"]) | ||||
| 145 | + M[prefix + "PostW"] = signed(100 * s["post_w"]["mean"]) | ||||
| 146 | + | ||||
| 147 | + | ||||
| 148 | +sig("z_KW", "mKw") | ||||
| 149 | +sig("z_KWlog", "mKwLog") | ||||
| 150 | +sig("z_ALT", "mStrip") | ||||
| 151 | +sig("z_ALTcore", "mCore") | ||||
| 152 | +sig("z_CYBERstrip", "mCyberStrip") | ||||
| 153 | +M["mKwShareZero"] = pct(S["z_KW"]["share_zero"]) | ||||
| 154 | +hr = con["horse_race_w"] | ||||
| 155 | +M["mHrComposVsKw"] = signed(hr["composite_vs_keyword"]["z_AI_COMPOS"]["slope_pct"], 2) | ||||
| 156 | +M["mHrComposVsKwT"] = num(hr["composite_vs_keyword"]["z_AI_COMPOS"]["t"]) | ||||
| 157 | +M["mHrKw"] = signed(hr["composite_vs_keyword"]["z_KW"]["slope_pct"], 2) | ||||
| 158 | +M["mHrKwT"] = num(hr["composite_vs_keyword"]["z_KW"]["t"]) | ||||
| 159 | +M["mHrComposVsStrip"] = signed(hr["composite_vs_stripped"]["z_AI_COMPOS"]["slope_pct"], 2) | ||||
| 160 | +M["mHrComposVsStripT"] = num(hr["composite_vs_stripped"]["z_AI_COMPOS"]["t"]) | ||||
| 161 | +M["mHrStrip"] = signed(hr["composite_vs_stripped"]["z_ALT"]["slope_pct"], 2) | ||||
| 162 | +M["mHrStripT"] = num(hr["composite_vs_stripped"]["z_ALT"]["t"]) | ||||
| 163 | +exu = con["ex_utilities_energy"] | ||||
| 164 | +M["mExUtilFullW"] = signed(100 * exu["full_w"]["mean"]) | ||||
| 165 | +M["mExUtilFullWT"] = num(exu["full_w"]["t"]) | ||||
| 166 | +M["mExUtilN"] = thousands(exu["n"]) | ||||
| 167 | +M["mUtilShareQfive"] = pct(con["utilities_share_q5_vs_all"][0]) | ||||
| 168 | +M["mUtilShareAll"] = pct(con["utilities_share_q5_vs_all"][1]) | ||||
| 169 | +M["mITShareQfive"] = pct(con["q5_sector_shares"]["Information Technology"], 0) | ||||
| 170 | + | ||||
| 171 | +# ---------------------------------------------------------------- survivorship bounding (r3k_18) | ||||
| 172 | +sv = load("results/r3k_v2/results_surv.json") | ||||
| 173 | +b = sv["bounding"] | ||||
| 174 | +req = b["required_exit_rate_difference_e1_minus_e5"] | ||||
| 175 | +M["mBoundExitThirty"] = pct(req["r_x=-0.3"]) | ||||
| 176 | +M["mBoundExitHalf"] = pct(req["r_x=-0.5"]) | ||||
| 177 | +M["mBoundExitFull"] = pct(req["r_x=-1.0"]) | ||||
| 178 | +small = {k: v for k, v in b["measured_annualised_exit_small_tercile"].items() if k >= "2019"} | ||||
| 179 | +large = {k: v for k, v in b["measured_annualised_exit_large_tercile"].items() if k >= "2019"} | ||||
| 180 | +M["mExitSmallMin"] = pct(min(small.values())) | ||||
| 181 | +M["mExitSmallMax"] = pct(max(small.values())) | ||||
| 182 | +M["mExitLargeMin"] = pct(min(large.values())) | ||||
| 183 | +M["mExitLargeMax"] = pct(max(large.values())) | ||||
| 184 | +ratios = [small[k] / large[k] for k in small if k in large] | ||||
| 185 | +M["mExitRatioMin"] = num(min(ratios), 1) | ||||
| 186 | +M["mExitRatioMax"] = num(max(ratios), 1) | ||||
| 187 | +M["mnIWVsnapshotsSinceNineteen"] = str(len([k for k in sv["snapshots"] if str(k)[:4] >= "2019"]) if isinstance(sv["snapshots"], (list, dict)) else "") | ||||
| 188 | +tm = sv["then_members_present"] | ||||
| 189 | +if isinstance(tm, dict): | ||||
| 190 | + shares = [v.get("share_members") for v in tm.values() if isinstance(v, dict) and v.get("share_members") is not None] | ||||
| 191 | +elif isinstance(tm, list): | ||||
| 192 | + shares = [v.get("share_members") for v in tm if isinstance(v, dict) and v.get("share_members") is not None] | ||||
| 193 | +else: | ||||
| 194 | + shares = [] | ||||
| 195 | +if shares: | ||||
| 196 | + M["mThenMembersMin"] = pct(min(shares), 0) | ||||
| 197 | + M["mThenMembersMax"] = pct(max(shares), 0) | ||||
| 198 | +es = sv.get("entry_side", {}) | ||||
| 199 | +if isinstance(es, dict) and "entrant_premium_w_pct" in es: | ||||
| 200 | + M["mEntrantPremW"] = signed(es["entrant_premium_w_pct"]) | ||||
| 201 | + | ||||
| 202 | +# ---------------------------------------------------------------- break placebos (r3k_16) | ||||
| 203 | +r2 = load("results/r3k_v2/results2.json") | ||||
| 204 | +pb = r2["placebo_breaks"] | ||||
| 205 | +cb = pb["permutation"]["contiguous_blocks"] | ||||
| 206 | +M["mContigAboveRaw"] = str(sum(1 for k, v in cb.items() if k != "2022" and v > cb["2022"])) | ||||
| 207 | +M["mContigN"] = str(len(cb) - 1) | ||||
| 208 | +cbw = pb["winsorized"]["permutation"]["contiguous_blocks"] | ||||
| 209 | +M["mContigAboveW"] = str(sum(1 for k, v in cbw.items() if k != "2022" and v > cbw["2022"])) | ||||
| 210 | +M["mContigBestYear"] = max((k for k in cb if k != "2022"), key=lambda k: cb[k]) | ||||
| 211 | +M["mContigBestDiff"] = signed(100 * cb[M["mContigBestYear"]]) | ||||
| 212 | +M["mPermSubsets"] = str(pb["permutation"]["n_subsets"]) | ||||
| 213 | +M["mSupWaldCVfive"] = num(pb["sup_wald"]["cv05_simulated"], 1) | ||||
| 214 | +ck = r2["cluster_k"]["reversal"] | ||||
| 215 | +M["mRevWithWithoutTwelve"] = signed(ck["high_language_with_vs_without_projects"]["month_1_12_w"]["mean_pct"], 2) | ||||
| 216 | +M["mRevWithWithoutTwelveT"] = num(ck["high_language_with_vs_without_projects"]["month_1_12_w"]["t"]) | ||||
| 217 | +M["mRevWithWithoutTwentyFour"] = signed(ck["high_language_with_vs_without_projects"]["month_13_24_w"]["mean_pct"], 2) | ||||
| 218 | +M["mRevWithWithoutTwentyFourT"] = num(ck["high_language_with_vs_without_projects"]["month_13_24_w"]["t"]) | ||||
| 219 | +M["mRevNhighWithProjects"] = thousands(ck["high_language_with_vs_without_projects"]["n_high_language_with_projects"]) | ||||
| 220 | +M["mRevNhigh"] = thousands(ck["high_language_with_vs_without_projects"]["n_high_language"]) | ||||
| 221 | +M["mRevComposThirteenTwentyFourW"] = signed(100 * ck["Q5_minus_Q1_month_13_24"]["w"]["mean"]) | ||||
| 222 | +M["mRevComposThirteenTwentyFourWT"] = num(ck["Q5_minus_Q1_month_13_24"]["w"]["t"]) | ||||
| 223 | +ft = r2["filing_type"] | ||||
| 224 | +if "wald_equal_slopes_w" in ft: | ||||
| 225 | + M["mFormWaldChi"] = num(ft["wald_equal_slopes_w"].get("stat", float("nan")), 2) | ||||
| 226 | +dt = r2["did_eventtime"] | ||||
| 227 | +M["mEvtRefSlope"] = signed(100 * dt["base_slope_ref_year"]) | ||||
| 228 | +M["mEvtRefT"] = num(dt["base_t"]) | ||||
| 229 | +rows_dt = {r["year"]: r for r in dt["rows"]} | ||||
| 230 | +M["mEvtDiffTwentyTwoT"] = num(rows_dt[2022]["diff_from_ref"] / rows_dt[2022]["diff_se"]) | ||||
| 231 | +M["mEvtDiffTwentyT"] = num(rows_dt[2020]["diff_from_ref"] / rows_dt[2020]["diff_se"]) | ||||
| 232 | +M["mEvtDiffTwentyEighteen"] = signed(100 * rows_dt[2018]["diff_from_ref"]) | ||||
| 233 | +M["mEvtDiffTwenty"] = signed(100 * rows_dt[2020]["diff_from_ref"]) | ||||
| 234 | +M["mEvtN"] = thousands(rows_dt[2022]["n"]) | ||||
| 235 | + | ||||
| 236 | +# ---------------------------------------------------------------- market-cap diagnostics (r3k_14) | ||||
| 237 | +mc = load("results/r3k_v2/mktcap_diag.json") | ||||
| 238 | +M["mAaplCapVone"] = num(mc["AAPL_cohort2010_cap_v1_usd"] / 1e9, 1) | ||||
| 239 | +M["mAaplRankVone"] = str(int(mc["AAPL_cohort2010_rank_v1"])) | ||||
| 240 | +M["mAaplCapVtwo"] = str(int(round(mc["AAPL_cohort2010_cap_v2_usd"] / 1e9))) | ||||
| 241 | +M["mAaplRankVtwo"] = str(int(mc["AAPL_cohort2010_rank_v2"])) | ||||
| 242 | +M["mLogCapMaxVone"] = num(mc["log_cap_min_max_v1"][1], 1) | ||||
| 243 | +M["mAepCapVoneExp"] = str(int(math.floor(math.log10(mc["AEP_2010"]["cap_v1"])))) | ||||
| 244 | +M["mAepCapVoneMant"] = num(mc["AEP_2010"]["cap_v1"] / 10 ** math.floor(math.log10(mc["AEP_2010"]["cap_v1"])), 1) | ||||
| 245 | +M["mAepCapVtwo"] = num(mc["AEP_2010"]["cap_v2"] / 1e9, 1) | ||||
| 246 | +M["mnCapsGtThreeTrillionVone"] = str(mc["n_caps_gt_3e12_v1"]) | ||||
| 247 | +M["mnCapsGtThreeTrillionVtwo"] = str(mc["n_caps_gt_3e12"]) | ||||
| 248 | +M["mShareNoCapVone"] = pct(mc["share_est_without_cap_v1"]) | ||||
| 249 | +M["mShareNoCapVtwo"] = pct(mc["share_est_without_cap_v2"]) | ||||
| 250 | +M["mnSizeGroupChange"] = thousands(mc["n_firm_years_changing_size_group"]) | ||||
| 251 | + | ||||
| 252 | +# ---------------------------------------------------------------- BEFORE values from the committed baseline (HEAD 3cf7560) | ||||
| 253 | +r1 = load("results/r3k/results.json") | ||||
| 254 | +r2v1 = load("results/r3k/results2.json") | ||||
| 255 | +ev1 = load("results/r3k/results_event.json") | ||||
| 256 | +M["mvoneNobs"] = thousands(r1["n_obs"]) | ||||
| 257 | +M["mvoneNfirms"] = thousands(r1["n_firms"]) | ||||
| 258 | +M["mvoneSpreadFull"] = signed(100 * r1["sorts"]["AI_COMPOS_EW"]["Q5_minus_Q1"]) | ||||
| 259 | +M["mvoneSpreadFullT"] = num(r1["sorts"]["AI_COMPOS_EW"]["t_stat"]) | ||||
| 260 | +M["mvoneSpreadFullVW"] = signed(100 * r1["sorts"]["AI_COMPOS_VW"]["Q5_minus_Q1"]) | ||||
| 261 | +M["mvoneSpreadFullVWT"] = num(r1["sorts"]["AI_COMPOS_VW"]["t_stat"]) | ||||
| 262 | +M["mvoneVWQone"] = signed(100 * r1["sorts"]["AI_COMPOS_VW"]["Q1"]) | ||||
| 263 | +M["mvoneVWQfive"] = signed(100 * r1["sorts"]["AI_COMPOS_VW"]["Q5"]) | ||||
| 264 | +M["mvoneFMcompos"] = signed(100 * r1["fama_macbeth"]["compos"]["z_AI_COMPOS"]["mean_coef"], 2) | ||||
| 265 | +M["mvoneFMcomposT"] = num(r1["fama_macbeth"]["compos"]["z_AI_COMPOS"]["t"]) | ||||
| 266 | +sc = r2v1["sector_controls"]["fm_full"] | ||||
| 267 | +M["mvoneFMsector"] = signed(sc["slope_sector_pct"], 2) | ||||
| 268 | +M["mvoneFMsectorT"] = num(sc["t_sector"]) | ||||
| 269 | +M["mvoneSectorNeutralFull"] = signed(100 * r2v1["sector_neutral"]["full"]["mean"]) | ||||
| 270 | +M["mvoneSectorNeutralFullT"] = num(r2v1["sector_neutral"]["full"]["t"]) | ||||
| 271 | +M["mvoneSpreadPre"] = signed(r2v1["cohorts"]["pre_mean_pct"]) | ||||
| 272 | +M["mvoneSpreadPost"] = signed(r2v1["cohorts"]["post_mean_pct"]) | ||||
| 273 | +M["mvonePooledPost"] = signed(r2v1["cohorts"]["pooled_post"]["coef_pct"]) | ||||
| 274 | +M["mvonePooledPostT"] = num(r2v1["cohorts"]["pooled_post"]["t"]) | ||||
| 275 | +M["mvonePooledPostW"] = signed(r2v1["cohorts"]["pooled_post_wins"]["coef_pct"]) | ||||
| 276 | +M["mvonePooledPostExTwentyFour"] = signed(r2v1["cohorts"]["pooled_post_excl_2024"]["coef_pct"]) | ||||
| 277 | +M["mvonePooledPostExTwentyFourT"] = num(r2v1["cohorts"]["pooled_post_excl_2024"]["t"]) | ||||
| 278 | +M["mvoneCohortTwentyTwo"] = signed(r2v1["cohorts"]["cohort_spreads_pct"]["2022"]) | ||||
| 279 | +M["mvoneEntrantPrem"] = signed(r2v1["entrants"]["entrant_minus_seasoned_fwd_pct"]) | ||||
| 280 | +g = r1["groups"] | ||||
| 281 | +M["mvoneLargePre"] = signed(100 * g["large_top1000"]["pre"]["mean"]) if "pre" in g["large_top1000"] else "" | ||||
| 282 | +M["mvoneLargePost"] = signed(100 * g["large_top1000"]["post"]["mean"]) if "post" in g["large_top1000"] else "" | ||||
| 283 | +M["mvoneSmallPre"] = signed(100 * g["small_rest"]["pre"]["mean"]) if "pre" in g["small_rest"] else "" | ||||
| 284 | +M["mvoneSmallPost"] = signed(100 * g["small_rest"]["post"]["mean"]) if "post" in g["small_rest"] else "" | ||||
| 285 | +M["mvoneEvFullHL"] = signed(ev1["car_1y"]["H_minus_L"]) | ||||
| 286 | +M["mvoneEvFullWelch"] = num(ev1["car_1y"]["t_welch"]) | ||||
| 287 | +M["mvoneEvLargeHL"] = signed(ev1["by_size"]["large"]["H_minus_L_1y"]) | ||||
| 288 | +M["mvoneEvLargeWelch"] = num(ev1["by_size"]["large"]["t_welch"]) | ||||
| 289 | +M["mvoneEvSmallHL"] = signed(ev1["by_size"]["small"]["H_minus_L_1y"]) | ||||
| 290 | +M["mvoneEvSmallWelch"] = num(ev1["by_size"]["small"]["t_welch"]) | ||||
| 291 | +M["mvoneEvSixtyHL"] = signed(ev1["car_60d"]["H_minus_L"]) | ||||
| 292 | +M["mvoneEvN"] = thousands(ev1["n_firms"]) | ||||
| 293 | +sp = r2v1["spid"]["z_SPID_TECH"] | ||||
| 294 | +M["mvoneSpidT"] = num(abs(sp["z_SPID_TECH:post"]["t"])) | ||||
| 295 | +M["mvoneSpidPostCoefExp"] = str(int(math.floor(math.log10(abs(sp["post"]["coef_pct"]))))) | ||||
| 296 | +M["mvoneDidCont"] = signed(100 * r2v1["did"]["continuous"]["coef"], 2) | ||||
| 297 | +M["mvoneDidContT"] = num(r2v1["did"]["continuous"]["t"]) | ||||
| 298 | +M["mvoneDidBin"] = signed(100 * r2v1["did"]["binary"]["coef"], 2) | ||||
| 299 | +M["mvoneDidBinT"] = num(r2v1["did"]["binary"]["t"]) | ||||
| 300 | +evt1 = {r["year"]: r for r in r2v1["did_eventtime"]} | ||||
| 301 | +M["mvoneEvtTwentyOne"] = signed(100 * evt1[2021]["coef"]) | ||||
| 302 | +M["mvoneEvtTwentyOneSE"] = num(100 * evt1[2021]["se"], 1) | ||||
| 303 | +M["mvoneEvtTwentyTwo"] = signed(100 * evt1[2022]["coef"]) | ||||
| 304 | +M["mvoneEvtTwentyFour"] = signed(100 * evt1[2024]["coef"]) | ||||
| 305 | +M["mvoneSpreadPostTnaive"] = num(r1["subperiod_spread"]["AI_COMPOS"]["post"]["t"]) if "post" in r1["subperiod_spread"]["AI_COMPOS"] else "" | ||||
| 306 | +M["mvoneSpidAnyPost"] = signed(r2v1["spid"]["spread_ai_any_post"]["post"]["mean_pct"]) | ||||
| 307 | +M["mvoneSpidAnyPostT"] = num(r2v1["spid"]["spread_ai_any_post"]["post"]["t"]) | ||||
| 308 | +M["mvoneSpidPre"] = signed(100 * r1["subperiod_spread"]["SPID_TECH"]["pre"]["mean"]) | ||||
| 309 | +M["mvoneSpidPreT"] = num(r1["subperiod_spread"]["SPID_TECH"]["pre"]["t"]) | ||||
| 310 | +M["mvoneSpidLSmean"] = signed(r1["ls_alpha"]["SPID_TECH"]["full"]["mean_ls_ann_pct"], 2) | ||||
| 311 | +M["mvonePlaceboRank"] = str(r2v1["placebo_breaks"]["rank_of_2022"]) if "placebo_breaks" in r2v1 else "" | ||||
| 312 | + | ||||
| 313 | +# ---------------------------------------------------------------- SPID extractor provenance (L8) | ||||
| 314 | +meta = load("results/r3k_v2/validation/spid_sample_meta.json") | ||||
| 315 | +M["mSpidModel"] = ", ".join(meta["extractor_provenance_from_code_snapshot"]["model"]).replace("_", "\\_") | ||||
| 316 | +M["mSpidSampleN"] = str(meta["n_sample"]) | ||||
| 317 | +M["mSpidUniverseMentions"] = thousands(meta["n_universe_mentions"]) | ||||
| 318 | +M["mSpidEvidenceShare"] = pct(meta["share_evidence_found"]) | ||||
| 319 | +M["mSpidConfMedian"] = num(meta["confidence_quantiles"]["0.5"], 1) | ||||
| 320 | +M["mSpidDbDate"] = meta["db_mtime"][:10] | ||||
| 321 | +M["mSpidStrata"] = str(len(meta["strata_allocation"])) | ||||
| 322 | + | ||||
| 323 | +# ---------------------------------------------------------------- hygiene summary (r3k_13) | ||||
| 324 | +hy = load("results/r3k_v2/hygiene_summary.json") | ||||
| 325 | +M["mHygieneConfirmedMonthly"] = str(hy["verdicts_monthly"]["confirmed"]) | ||||
| 326 | +M["mHygieneConfirmedFY"] = str(hy["verdicts_firm_year"]["confirmed"]) | ||||
| 327 | + | ||||
| 328 | + | ||||
| 329 | +# ---------------------------------------------------------------- winsorized rows of results.json v2 (r3k_15) | ||||
| 330 | +rv = load("results/r3k_v2/results.json") | ||||
| 331 | +so = rv["sorts"] | ||||
| 332 | +M["mriskFullW"] = signed(100 * so["AI_RISK_EW_w"]["Q5_minus_Q1"]) | ||||
| 333 | +M["mriskFullWT"] = num(so["AI_RISK_EW_w"]["t_stat"]) | ||||
| 334 | +M["mriskFullWTplain"] = num(so["AI_RISK_EW_w"]["t_plain"]) | ||||
| 335 | +M["mriskFull"] = signed(100 * so["AI_RISK_EW"]["Q5_minus_Q1"]) | ||||
| 336 | +M["mriskFullT"] = num(so["AI_RISK_EW"]["t_stat"]) | ||||
| 337 | +M["mriskTopOnePct"] = signed(100 * so["AI_RISK_EW"]["top1pct_contribution"]) | ||||
| 338 | +M["mtenkFullW"] = signed(100 * so["AI_10k_EW_w"]["Q5_minus_Q1"]) | ||||
| 339 | +M["mtenkFullWT"] = num(so["AI_10k_EW_w"]["t_stat"]) | ||||
| 340 | +M["mformwFullWsort"] = signed(100 * so["AI_formw_EW_w"]["Q5_minus_Q1"]) | ||||
| 341 | +M["mformwFullWsortT"] = num(so["AI_formw_EW_w"]["t_stat"]) | ||||
| 342 | +M["mcyberFullW"] = signed(100 * so["CYBER_EW_w"]["Q5_minus_Q1"]) | ||||
| 343 | +M["mcyberFullWT"] = num(so["CYBER_EW_w"]["t_stat"]) | ||||
| 344 | +M["mcomposTopOnePctW"] = signed(100 * so["AI_COMPOS_EW_w"].get("top1pct_contribution", float("nan"))) | ||||
| 345 | +sp = rv["subperiod_spread"] | ||||
| 346 | +for key, name in (("AI_10k", "tenk"), ("AI_RISK", "risk"), ("CYBER", "cyber"), ("AI_formw", "formw"), ("AI_PC1", "pca")): | ||||
| 347 | + blk = sp[key] | ||||
| 348 | + for per in ("pre", "post"): | ||||
| 349 | + w = blk.get(per + "_w") | ||||
| 350 | + if w: | ||||
| 351 | + M[f"m{name}{per.capitalize()}Wsp"] = signed(100 * w["mean"]) | ||||
| 352 | + M[f"m{name}{per.capitalize()}WspT"] = num(w["t"]) | ||||
| 353 | +g2 = rv["groups"] | ||||
| 354 | +for key, name in (("large_pct42", "largeFortyTwo"), ("small_pct42", "smallFiftyEight")): | ||||
| 355 | + blk = g2[key] | ||||
| 356 | + M[f"m{name}N"] = thousands(blk["n"]) | ||||
| 357 | + M[f"m{name}Firms"] = thousands(blk["firms"]) | ||||
| 358 | + for per in ("pre", "post", "full"): | ||||
| 359 | + w = blk.get(per + "_w") | ||||
| 360 | + if w: | ||||
| 361 | + M[f"m{name}{per.capitalize()}W"] = signed(100 * w["mean"]) | ||||
| 362 | + M[f"m{name}{per.capitalize()}WT"] = num(w["t"]) | ||||
| 363 | + if "post_loo_w" in blk: | ||||
| 364 | + M[f"m{name}PostExTwentyFourW"] = signed(100 * blk["post_loo_w"]["2024"]) | ||||
| 365 | +ff = r2["filing_type"]["fm_forms_w"]["horse_race"] | ||||
| 366 | +for key, name in (("z_AI_10k", "TenK"), ("z_AI_10q", "TenQ"), ("z_AI_8k", "EightK")): | ||||
| 367 | + if key in ff: | ||||
| 368 | + M[f"mhr{name}W"] = signed(ff[key]["slope_pct"], 2) | ||||
| 369 | + M[f"mhr{name}WT"] = num(ff[key]["t"]) | ||||
| 370 | +en = r2["entrants"] | ||||
| 371 | +if "pre_w" in en["seasoned"]: | ||||
| 372 | + M["mseasonedPreW"] = signed(100 * en["seasoned"]["pre_w"]["mean"]) | ||||
| 373 | + M["mseasonedPostW"] = signed(100 * en["seasoned"]["post_w"]["mean"]) if "post_w" in en["seasoned"] else "" | ||||
| 374 | +if "entrant_minus_seasoned_fwd_w_pct" in en: | ||||
| 375 | + M["mentrantPremW"] = signed(en["entrant_minus_seasoned_fwd_w_pct"]) | ||||
| 376 | +for k in ("entrant_minus_seasoned_fwd_pct",): | ||||
| 377 | + if k in en: M["mentrantPremRaw"] = signed(en[k]) | ||||
| 378 | + | ||||
| 379 | +# ---------------------------------------------------------------- cohort sizes, attrition 2019, 2021 split, contiguous block | ||||
| 380 | +nby = rv["n_by_year"] | ||||
| 381 | +M["mnObsFirstCohort"] = thousands(nby[min(nby)]) | ||||
| 382 | +M["mnObsLastCohort"] = thousands(nby[max(nby)]) | ||||
| 383 | +piv = sv["attrition_pivot_absent_share"] | ||||
| 384 | +M["mattritionSmallNineteen"] = pct(piv["20190531"]["small"], 0) | ||||
| 385 | +M["mattritionLargeNineteen"] = pct(piv["20190531"]["large"], 0) | ||||
| 386 | +M["mattritionSmallTwentyFour"] = pct(piv["20241206"]["small"], 0) | ||||
| 387 | +M["mattritionLargeTwentyFour"] = pct(piv["20241206"]["large"], 0) | ||||
| 388 | +cw = r2["cohorts_w"] | ||||
| 389 | +cs = cw.get("cohort_spreads_pct", cw) | ||||
| 390 | +M["mcohortTwentyOneW"] = signed(cs["2021"]) if isinstance(cs.get("2021"), (int, float)) else "" | ||||
| 391 | +br = r2["breaks"] | ||||
| 392 | +alt = br.get("split_2021") | ||||
| 393 | +if alt: | ||||
| 394 | + M["mbreakTwentyOnePreW"] = signed(100 * alt["pre_w"]["mean"]) | ||||
| 395 | + M["mbreakTwentyOnePreWT"] = num(alt["pre_w"]["t"]) | ||||
| 396 | + M["mbreakTwentyOnePostW"] = signed(100 * alt["post_w"]["mean"]) | ||||
| 397 | + M["mbreakTwentyOnePostWT"] = num(alt["post_w"]["t"]) | ||||
| 398 | + pdw = alt["pooled_diff_w"] | ||||
| 399 | + M["mbreakTwentyOneDiffW"] = signed(pdw["coef_pct"]) | ||||
| 400 | + M["mbreakTwentyOneDiffWT"] = num(pdw["t_firm"]) | ||||
| 401 | + M["mbreakTwentyOneDiffWTtw"] = num(pdw["t_twoway"]) | ||||
| 402 | +by = M["mContigBestYear"] | ||||
| 403 | +M["mContigBestBlock"] = f"{by}--{int(by) + 2}" | ||||
| 404 | + | ||||
| 405 | +# ---------------------------------------------------------------- N1 corpus macros re-emitted with letter-only names | ||||
| 406 | +# results/n1/corpus_macros.tex (code/n1/n1_01_corpus_counts.py) uses digits in macro names (\mnFilings10K), | ||||
| 407 | +# which LaTeX cannot parse; the same values are re-emitted here as \mnFilingsTenK etc. | ||||
| 408 | +import re as _re | ||||
| 409 | +_digit_map = {"10K": "TenK", "10Q": "TenQ", "8K": "EightK"} | ||||
| 410 | +for line in (ROOT / "results/n1/corpus_macros.tex").read_text().splitlines(): | ||||
| 411 | + m = _re.match(r"\\newcommand\{\\(\w+)\}\{(.*)\}$", line.strip()) | ||||
| 412 | + if not m: | ||||
| 413 | + continue | ||||
| 414 | + name, val = m.group(1), m.group(2) | ||||
| 415 | + for d, w in _digit_map.items(): | ||||
| 416 | + name = name.replace(d, w) | ||||
| 417 | + if name not in M and name not in ("mnFilingsCorpus", "mnIssuersCorpus"): | ||||
| 418 | + M[name] = val | ||||
| 419 | + | ||||
| 420 | +# ---------------------------------------------------------------- write | ||||
| 421 | +M = {k: v for k, v in M.items() if v not in ("", None)} | ||||
| 422 | +bad = [k for k in M if not k.isalpha()] | ||||
| 423 | +assert not bad, f"macro names must be letters only: {bad}" | ||||
| 424 | +# never redefine a macro that tables/macros.tex (Agent 9 / r3k_20) already provides: theirs wins | ||||
| 425 | +import re as _re2 | ||||
| 426 | +_theirs = set(_re2.findall(r"\\newcommand\{\\(\w+)\}", (ROOT / "paper/tables/macros.tex").read_text())) | ||||
| 427 | +_dup = sorted(k for k in M if k in _theirs) | ||||
| 428 | +if _dup: | ||||
| 429 | + print("skipping names already defined in tables/macros.tex:", _dup) | ||||
| 430 | +M = {k: v for k, v in M.items() if k not in _theirs} | ||||
| 431 | +lines = ["% Generated by scripts/stageE_writing_macros.py (Stage E, Agent 10) — do not edit by hand.", | ||||
| 432 | + "% Sources: results/n1/*, results/r3k_v2/*, results/r3k/* (committed baseline 3cf7560 for the mvone* BEFORE values)."] | ||||
| 433 | +for k, v in M.items(): | ||||
| 434 | + lines.append(f"\\newcommand{{\\{k}}}{{{v}}}") | ||||
| 435 | +OUT.write_text("\n".join(lines) + "\n") | ||||
| 436 | +print(f"wrote {OUT.relative_to(ROOT)} with {len(M)} macros") | ||||
| 437 | |||||