financial-text-nlp¶
Sentiment and stance measurement for 10-K/10-Q MD&A, risk factors, 8-Ks, earnings-call transcripts and central-bank communication, using FinBERT (ProsusAI) as the main classifier and FinVADER as a transparent lexicon robustness check. Its iron law is to define the unit of text and the label before scoring. Scores are tied to accession or transcript IDs, boilerplate dilution is flagged, and the research design must state whether sentiment enters as outcome, covariate or instrument, with NLP error treated as measurement error.
analysisMIT (repo LICENSE, "Copyright (c) 2026 John Barrios"). The vendored third-party skills keep their own terms: the Anthropic document skills say "Proprietary. LICENSE.txt has complete terms", and the K-Dense skills carry per-library licence lines.data-analysisBarrios Skills — John Barrios's curated workflow for economists and accountants. Prioritize reproducible empirical work, clear identification language, and journal-ready output.
Financial & economic text NLP¶
When to use¶
- Tone of MD&A, risk factors, 8-K text, or ESG/boilerplate sections
- Earnings-call transcripts (prepare/Q&A separately when possible)
- Central-bank or supervisory communications (hawkish/dovish stance)
- Building panel covariates (firm–year sentiment) for regressions in
pyfixestor Stata
Not for: casual product reviews or generic Twitter sentiment without a finance lexicon/model.
Tool map¶
| Task | Tool | Notes |
|---|---|---|
| General financial tone | FinBERT (ProsusAI) | Sentence/chunk classification: positive/negative/neutral |
| Lexicon baseline | FinVADER | Fast, transparent; good robustness check |
| Central-bank stance | CentralBankRoBERTa, WorldCentralBanks | Domain labels ≠ FinBERT polarity |
| Econ NER / domain LM | EconBERTa | Entity/paper domain — not a drop-in sentiment score |
| Document → text | sec-edgar, markitdown/Docling |
Get clean text before scoring |
Iron law: define the unit and the label¶
- Unit of analysis: sentence, paragraph, section, or full document (document scores need aggregation rules)
- Label meaning: polarity ≠ hawkishness ≠ forward-looking statements
- Train/test hygiene: do not tune thresholds on the same sample used for causal estimates
- Inspect: print 10 scored excerpts before merging to Compustat
Minimal FinBERT pattern¶
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import torch
model_id = "ProsusAI/finbert"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForSequenceClassification.from_pretrained(model_id)
def score_texts(texts: list[str], max_length: int = 256):
inputs = tokenizer(
texts, padding=True, truncation=True, max_length=max_length, return_tensors="pt"
)
with torch.no_grad():
probs = torch.nn.functional.softmax(model(**inputs).logits, dim=-1)
# labels: positive, negative, neutral (confirm order via model.config.id2label)
return probs
Aggregate to firm–year with an explicit rule (mean of sentence scores; or net positivity = pos − neg). Document the rule in the paper.
Accounting / finance research notes¶
- Boilerplate: length and legalese dilute tone — consider section fixed effects or residuals
- Attribution: link scores to accession IDs / transcript IDs for replication
- Identification: sentiment as outcome vs covariate vs instrument — state which; NLP error is measurement error
- Robustness: report FinBERT + lexicon (FinVADER) or alternative chunking
Checklist¶
- Text source and cleaning steps logged
- Model id + revision pinned
- Aggregation rule written down
- Hand-checked excerpts saved
- Merge keys to firm identifiers validated (CIK / gvkey / ticker map)