Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ adheres to [Semantic Versioning](https://semver.org/).

### Changed

- **Resolver docs: Mais Retorno Free limits.** `docs/RESOLVER.md` now
states that the optional cascade step uses the operator's own account and
documents the public Free tier (500 credits/month shared by REST+MCP,
variable per-call cost, 1-year history, 15 req/s, HTTP 429 when exhausted),
distinguishing REST API key vs MCP OAuth, with dated links to
[maisretorno.com/mcp](https://maisretorno.com/mcp) and
[developers.maisretorno.com](https://developers.maisretorno.com).
- **Distribution slug renamed `findata-br` → `openfindata`.** The PyPI
distribution name is now `openfindata` (`pip install openfindata`,
`pip install 'openfindata[b3]'`), aligning the package slug with the
Expand Down
44 changes: 40 additions & 4 deletions docs/RESOLVER.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,50 @@ Quando `confidence < ~0.9` ou status `candidate`, é gancho de revisão humana.

1. **openfindata** (primário, offline): seed curado + regras estruturais. Resolve
o test set sem rede.
2. **Mais Retorno MCP** (dados BR de fundo/CNPJ/classe CVM).
2. **Mais Retorno** (dados BR de fundo/CNPJ/classe CVM) — provider externo
opcional via API de dados / MCP; ver limites Free abaixo.
3. **outro provider** (CVM dados abertos / B3).
4. **web_search restrito** a `maisretorno.com`, `b3.com.br`,
`yahoofinance.com.br`, `debentures.com.br`.

Cada degrau preenche o que o anterior não trouxe e **baixa a confidence**;
`source` reflete a origem final; `cascade` loga o caminho. Os degraus 2 a 4 são
Comment on lines 101 to 102

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- candidate files ---'
git ls-files 'docs/RESOLVER.md' 'src/findata/resolver/engine.py' 'tests/*' | sed -n '1,120p'
printf '%s\n' '--- engine outline ---'
ast-grep outline src/findata/resolver/engine.py --view compact || true
printf '%s\n' '--- relevant documentation ---'
cat -n docs/RESOLVER.md | sed -n '80,125p'
printf '%s\n' '--- relevant engine section ---'
cat -n src/findata/resolver/engine.py | sed -n '1,180p'
printf '%s\n' '--- provider and confidence references ---'
rg -n -C 3 'confidence|cascade|source|provider|classification|classify' src/findata/resolver tests docs/RESOLVER.md

Repository: robertoecf/OpenFinData

Length of output: 50378


🏁 Script executed:

#!/bin/bash
set -eu
printf '%s\n' '--- resolver orchestration ---'
cat -n src/findata/resolver/engine.py | sed -n '640,740p'
printf '%s\n' '--- provider-focused tests ---'
cat -n tests/test_resolver.py | sed -n '285,340p'
printf '%s\n' '--- classification model ---'
cat -n src/findata/resolver/models.py | sed -n '145,180p'
printf '%s\n' '--- provider declarations and assignments ---'
rg -n -C 5 'AssetProvider|enriched|result = enriched|confidence|cascade|source' src/findata/resolver/engine.py tests/test_resolver.py
printf '%s\n' '--- static invariant verifier ---'
python3 - <<'PY'
from pathlib import Path
text = Path("src/findata/resolver/engine.py").read_text()
start = text.index("    for provider in providers or []:")
end = text.index("    return result", start)
loop = text[start:end]
checks = {
    "provider receives current result": "await provider(norm, result)" in loop,
    "provider result replaces current result": "result = enriched" in loop,
    "cascade is explicitly combined": "enriched.cascade = [*result.cascade, *enriched.cascade]" in loop,
    "confidence is explicitly reduced": any(
        token in loop for token in (
            "result.confidence -", "enriched.confidence -",
            "confidence = min(", "confidence -= ", "confidence *= ",
        )
    ),
}
for name, value in checks.items():
    print(f"{name}: {value}")
print("--- loop ---")
print(loop)
PY

Repository: robertoecf/OpenFinData

Length of output: 40636


Align the provider contract with the resolver behavior.

The resolver replaces the current AssetClassification with the provider result. It does not merge missing fields or reduce confidence; it only carries forward the returned enriched.cascade. Update both descriptions to document provider-controlled overrides, confidence, and cascade entries, or implement the documented merge behavior.

📍 Affects 2 files
  • docs/RESOLVER.md#L101-L102 (this comment)
  • src/findata/resolver/engine.py#L14-L15
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/RESOLVER.md` around lines 101 - 102, The resolver documentation and
provider contract are inconsistent: the resolver replaces AssetClassification
with provider results rather than merging fields or lowering confidence. Update
docs/RESOLVER.md lines 101-102 and the corresponding contract description in
src/findata/resolver/engine.py lines 14-15 to state that providers control
overrides and confidence, and that cascade entries are carried forward from
enriched.cascade; do not describe merge or confidence-reduction behavior unless
implementing it in the resolver.

um ponto de extensão injetável (`AssetProvider`), consultado só quando o
resultado do núcleo está fraco. No estado atual deste PR, **só o degrau 1 está
ligado** (os externos são stubs a conectar no deploy).
resultado do núcleo está fraco. Hoje **só o degrau 1 está ligado** (os externos
são stubs a conectar no deploy).

### Mais Retorno: plano Free e cotas

O openfindata **não embute** chave nem cota da Mais Retorno. Quem ligar o
degrau 2 no deploy usa a conta do operador. A API de dados tem dois canais
sobre o **mesmo saldo de créditos**
([developers.maisretorno.com](https://developers.maisretorno.com), conferido em
2026-08-12):

- **REST** (típico para um `AssetProvider` server-side): API key
(`X-Api-Key` / `Authorization: Bearer`) gerada em
[maisretorno.com/app/meu-perfil/api](https://maisretorno.com/app/meu-perfil/api).
- **MCP** (agente de IA): URL
`https://data.maisretorno.com/mr-data/v4/mcp`, autenticação **OAuth** (sem
api-key). Página de produto:
[maisretorno.com/mcp](https://maisretorno.com/mcp).

Limites relevantes do plano **Free** (permanente, sem cartão):

| Item | Free |
|---|---|
| Créditos | 500/mês no mesmo saldo REST+MCP |
| Custo por chamada | variável (ex.: search grátis; `asset-info`/quotes/`fund-class-subclass` = 1; stats/drawdown = 5; `wallet-detail` = 10; compare/backtest = 25) |
| Histórico | até 1 ano (planos pagos: histórico completo) |
| MCP | disponível no Free (mesmas classes/endpoints dos planos pagos) |
| Rate limit | 15 req/s em todos os planos |
| Cota esgotada | HTTP 429 até renovar o ciclo (aviso por email ~80%) |
Comment on lines +131 to +132

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

sed -n '90,145p' docs/RESOLVER.md
printf '\n--- external documentation ---\n'
curl -L --max-time 15 -sS https://developers.maisretorno.com/ | head -c 5000

Repository: robertoecf/OpenFinData

Length of output: 7888


🌐 Web query:

site:developers.maisretorno.com HTTP 429 quota upgrade renewal Mais Retorno API

💡 Result:

If you receive an HTTP 429 Too Many Requests error when using the Mais Retorno API, it indicates that your current credit balance for the billing cycle has been exhausted [1]. To resolve this issue, you have two options: 1. Automatic Renewal: Your credit balance will automatically reset upon the start of your next monthly billing cycle [1]. 2. Immediate Upgrade: You can regain access immediately by upgrading your current API plan [1]. To manage your consumption and avoid hitting these limits, the official documentation recommends respecting the Cache-Control headers returned by the API [1]. Each endpoint has a defined max-age for caching; by utilizing this, you can significantly reduce the number of unnecessary requests, thereby conserving your credit balance [1]. For instance, endpoints such as /quotes/{identifier} and /asset-info/{identifier} have a cache duration of 90 minutes [1].

Citations:


Include the plan-upgrade path for exhausted quotas.

Document that HTTP 429 ends when the cycle renews or the operator upgrades the plan.

Proposed wording
-| Cota esgotada | HTTP 429 até renovar o ciclo (aviso por email ~80%) |
+| Cota esgotada | HTTP 429 até renovar o ciclo ou fazer upgrade (aviso por email ~80%) |
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
| Rate limit | 15 req/s em todos os planos |
| Cota esgotada | HTTP 429 até renovar o ciclo (aviso por email ~80%) |
| Rate limit | 15 req/s em todos os planos |
| Cota esgotada | HTTP 429 até renovar o ciclo ou fazer upgrade (aviso por email ~80%) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/RESOLVER.md` around lines 131 - 132, Update the “Cota esgotada” entry in
RESOLVER.md to state that HTTP 429 ends when the billing cycle renews or when
the operator upgrades the plan, preserving the existing renewal behavior and
email-warning detail.

Source: MCP tools


Não trate 500 créditos como “500 resoluções”: um provider que chame stats ou
carteira consome bem mais por ativo. Fora do escopo dessa API/MCP (não usar
como fallback para esses ativos): CRI, CRA, FIDC, debêntures e ativos offshore.
Volume e profundidade de histórico sobem nos planos pagos; o rate limit por
segundo não.

## Test set (passa 100%, offline)

Expand All @@ -126,7 +160,9 @@ ligado** (os externos são stubs a conectar no deploy).

## Pendências antes de produção

- Conectar os providers externos reais (Mais Retorno MCP, web search restrito).
- Conectar os providers externos reais (Mais Retorno via REST/API key do
operador — Free = 500 créditos/mês no saldo compartilhado com o MCP —, e
web search restrito).
- Confirmação ISIN-level da incentivada (12.431) via ANBIMA/debentures.com.br no
degrau de cascata — hoje fica `candidate`.
- Ampliar o seed curado de ETFs conforme novos ETFs forem listados na B3.
18 changes: 18 additions & 0 deletions docs/SOURCES_WITH_AUTH.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,24 @@ Quirks da API ANBIMA (já validamos em testes ao vivo):



## Mais Retorno (cascata do resolver): Free com cota

Não é fonte core do openfindata — é um degrau opcional da cascata de
`resolve_asset` (`docs/RESOLVER.md`). O operador traz a própria conta; o
projeto não embute credenciais.

Plano **Free** permanente (sem cartão), conforme
[maisretorno.com/mcp](https://maisretorno.com/mcp) e
[developers.maisretorno.com](https://developers.maisretorno.com) (conferido em
2026-08-12): 500 créditos/mês no mesmo saldo para REST e MCP, histórico de até
1 ano, rate limit 15 req/s. Custo por operação é variável (não 1 crédito por
qualquer chamada). REST autentica com API key; MCP autentica com OAuth.
Cota esgotada → HTTP 429. Volume e histórico completo ficam nos planos pagos.

Trate como `free_logged_in` com cota mensal: self-serve, mas não anônimo e
não ilimitado. Detalhes e escopo (o que a API/MCP não cobre) estão em
`docs/RESOLVER.md`.

## Base dos Dados: grátis, mas com login/projeto do usuário

Base dos Dados não entra na mesma categoria da API autenticada da ANBIMA. O
Expand Down
6 changes: 4 additions & 2 deletions src/findata/resolver/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
2. **Structural rules** (this module) — name/ticker patterns that *are*
derivable: COE, debenture, CRA/CRI, bank paper, Tesouro, IE/global,
FII, FIA/Ações, Multimercado, FIDC/FIP, plain tickers.
3. **External providers** (optional, injected) — Mais Retorno MCP, CVM/B3,
3. **External providers** (optional, injected) — Mais Retorno, CVM/B3,
restricted web search. Not bundled here (they are client-side / networked);
the resolver takes a chain of async callbacks so a deployment can wire them.
Each step that fires lowers ``confidence`` and is appended to ``cascade``.
Mais Retorno uses the operator's own account/quota (see
``docs/RESOLVER.md``). Each step that fires lowers ``confidence`` and is
appended to ``cascade``.

The seed + rules layers are pure and offline, so the spec's test set resolves
deterministically with no network. ``source`` is ``"openfindata"`` for every
Expand Down
Loading