PT-BR Financial Sentiment Analysis
A BERT model fine-tuned for financial sentiment classification of Brazilian Portuguese news. The model is built upon FinBERT-PT-BR and predicts the sentiment conveyed by financial news headlines or short articles related to the Brazilian economy and capital markets.
Given a financial news text, the model assigns one of three sentiment labels:
- POSITIVE โ news suggesting a favorable impact or outlook for the financial market.
- NEGATIVE โ news suggesting an unfavorable impact or outlook for the financial market.
- NEUTRAL โ factual or informational news without a clear positive or negative financial sentiment.
The model was developed as part of an undergraduate thesis investigating whether sentiment indicators extracted from Brazilian financial news can improve quantitative asset allocation strategies for the Brazilian stock market.
Resources
Base model
This model is fine-tuned from FinBERT-PT-BR, developed by Lucas Leme.
- Hugging Face: https://huggingface.co/lucas-leme/FinBERT-PT-BR
Project repository
The complete training pipeline, manually annotated dataset, preprocessing scripts, and experiments used to produce this model are available at:
Dataset
The model was fine-tuned on a manually annotated subset of 624 Brazilian financial news articles sampled from a larger corpus containing approximately 56,000 financial news articles collected between 2016 and 2025.
| Split | Examples |
|---|---|
| Training | 399 |
| Validation | 100 |
| Test | 125 |
| Total | 624 |
The train/validation/test split was stratified by sentiment class.
The complete news corpus used as the source for manual annotation is publicly available as the PT-BR Financial News Dataset:
Evaluation
The final ensemble was evaluated once on an independent test set containing 125 manually annotated news articles.
| Model | Accuracy | Macro F1 |
|---|---|---|
| Base FinBERT-PT-BR | 0.336 | 0.325 |
| Fine-tuned Ensemble | 0.784 | 0.783 |
Compared to the original model, the fine-tuned ensemble achieved:
- +44.8 percentage points in Accuracy
- +45.8 percentage points in Macro-F1
Usage
The repository stores each seed checkpoint in a subfolder. Load one seed with subfolder:
from transformers import AutoTokenizer, BertForSequenceClassification
import torch
model_id = "lucasalmda/pt-br-financial-sentimental-analysis"
seed = "seed-789" # also available: "seed-123" and "seed-456"
tokenizer = AutoTokenizer.from_pretrained(model_id, subfolder=seed)
model = BertForSequenceClassification.from_pretrained(model_id, subfolder=seed)
model.eval()
id2label = {
0: "POSITIVE",
1: "NEGATIVE",
2: "NEUTRAL",
}
text = "Ibovespa fecha em alta com expectativa de corte na taxa Selic"
inputs = tokenizer(
text,
return_tensors="pt",
truncation=True,
max_length=512,
)
with torch.no_grad():
logits = model(**inputs).logits
prediction = logits.argmax(dim=-1).item()
print(id2label[prediction])
To reproduce the project ensemble, load the three seed subfolders, average the raw logits, and then apply argmax:
from transformers import AutoTokenizer, BertForSequenceClassification
import torch
model_id = "lucasalmda/pt-br-financial-sentimental-analysis"
seeds = ["seed-789", "seed-123", "seed-456"]
text = "Ibovespa fecha em alta com expectativa de corte na taxa Selic"
all_logits = []
for seed in seeds:
tokenizer = AutoTokenizer.from_pretrained(model_id, subfolder=seed)
model = BertForSequenceClassification.from_pretrained(model_id, subfolder=seed)
model.eval()
inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
with torch.no_grad():
all_logits.append(model(**inputs).logits)
ensemble_logits = torch.stack(all_logits).mean(dim=0)
prediction = ensemble_logits.argmax(dim=-1).item()
print({0: "POSITIVE", 1: "NEGATIVE", 2: "NEUTRAL"}[prediction])
Model tree for lucasalmda/pt-br-financial-sentiment-analysis
Base model
lucas-leme/FinBERT-PT-BR