O histórico OHLC do Crivo é retornado da data mais recente para a mais antiga e usa um cursor exclusivo para continuar a consulta. Envie o cursor devolvido pela página anterior; não acrescente um dia, não subtraia segundos e não use o número da página como substituto.
No teste com TOTS3, duas páginas de cinco registros produziram dez datas distintas, de 28 de setembro a 9 de outubro de 2026. Conferimos esses registros contra os primeiros dez de uma consulta maior: datas, preços e volumes coincidiram.
Leia o cursor devolvido, sem reconstruí-lo
A resposta de /ohlc/TOTS3 contém o array ohlc_data e o objeto pagination. O cursor é um timestamp UTC numérico usado para continuar o histórico. A página seguinte traz observações anteriores ao limite exclusivo.
O fato de a primeira resposta ter cinco registros não diz que só existem cinco no banco. Da mesma forma, uma data ausente não prova um erro de coleta: pode ser um dia sem pregão ou sem observação na cobertura disponível. Paginação e completude de mercado são verificações diferentes.
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, build_opener, HTTPRedirectHandler
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, *args):
return None
key = os.environ["CRIVO_API_KEY"].strip()
if not key:
raise ValueError("CRIVO_API_KEY está vazia")
client = build_opener(NoRedirect())
def get_json(path):
req = Request("https://api.crivo.ai" + path,
headers={"X-API-Key": key, "Accept": "application/json"})
try:
with client.open(req, timeout=30) as response:
return json.load(response)
except HTTPError as error:
raise RuntimeError(f"Consulta interrompida: HTTP {error.code}") from None
bars, dates, cursors = [], set(), set()
cursor = None
for page in range(2):
path = "/ohlc/TOTS3?limit=5"
if cursor is not None:
path += f"&cursor={cursor}"
data = get_json(path)
chunk = data["ohlc_data"]
if not chunk:
break
for row in chunk:
if row["date"] in dates:
raise ValueError("Data repetida entre páginas")
if row["close"] is None:
raise ValueError("Fechamento ausente")
dates.add(row["date"])
bars.append(row)
next_cursor = data["pagination"]["cursor"]
if next_cursor is None:
break
if next_cursor in cursors:
raise ValueError("Cursor repetido")
cursors.add(next_cursor)
cursor = next_cursor
bars.sort(key=lambda row: row["date"])
print(json.dumps(bars, ensure_ascii=False, indent=2))
O exemplo é deliberadamente limitado a duas páginas. Esse limite deixa o teste pequeno e reproduzível; não promete coletar todo o histórico da B3. Para uma janela maior, continue enquanto houver registros e um cursor novo, parando ao atingir a data desejada. Respeite os limites do acesso e interrompa em erro, em vez de tratar uma falha como fim do histórico.
Detecte repetição antes de ordenar
Um conjunto de datas detecta sobreposição entre páginas. Se a mesma data aparecer novamente, o código encerra com erro. Apenas remover a repetição esconderia uma falha de paginação ou uma alteração de cobertura durante a coleta.
Também confira a progressão do cursor. Um cursor que se repete pode criar um ciclo infinito em um coletor sem limite. A ordenação cronológica deve ocorrer depois dessas verificações, para transformar o formato de consulta em uma sequência adequada ao gráfico.
| Página | Mais antiga | Mais recente | Registros |
|---|---|---|---|
| 1 | 2026-10-05 | 2026-10-09 | 5 |
| 2 | 2026-09-28 | 2026-10-02 | 5 |
Na captura, a primeira página ia de 5 a 9 de outubro; a segunda, de 28 de setembro a 2 de outubro. Ambas vieram em ordem decrescente. Para o gráfico, reunimos as dez observações e ordenamos da mais antiga para a mais recente.
Ver dados do gráfico
| Referência | Fechamento bruto (R$ por ação) |
|---|---|
| 2026-09-28 | 33,98 |
| 2026-09-29 | 34,64 |
| 2026-09-30 | 33,43 |
| 2026-10-01 | 34,58 |
| 2026-10-02 | 35,35 |
| 2026-10-05 | 36,82 |
| 2026-10-06 | 38,46 |
| 2026-10-07 | 39,32 |
| 2026-10-08 | 40,81 |
| 2026-10-09 | 42,2 |
Confira null, campos e unidade
O exemplo exige uma data e um fechamento para cada ponto que será exportado. Se um fechamento for null, a execução para, em vez de convertê-lo em zero. Essa transformação criaria uma queda artificial de preço.
A verificação das datas também não substitui a conferência dos demais campos. Se sua aplicação usa máxima, mínima, abertura ou volume, valide os campos necessários e preserve a unidade retornada. Uma página com datas corretas ainda pode estar incompleta para o uso que você pretende fazer.
Preço bruto não é retorno total
Os fechamentos do gráfico são cotações brutas do histórico. A série não certifica ajustes por desdobramentos, grupamentos, proventos ou outros eventos. Sua variação não é automaticamente a rentabilidade de um investidor que manteve a ação.
Um estudo de patrimônio ou backtest precisa tratar eventos, dividendos, custos e critérios de universo, além de evitar informação futura. Conseguir paginar todos os registros não resolve esses requisitos econômicos.
Para uma integração de preços, guarde as páginas originais, parâmetros e horário da coleta. Assim, um gráfico pode ser reproduzido e uma divergência pode ser investigada. O teste de TOTS3 confirma o funcionamento do cursor naquele recorte; não certifica todo o histórico, todas as ações ou a continuidade de cada pregão.
Metodologia e limites
TOTS3, duas páginas de cinco barras OHLC com cursor UTC exclusivo; dez datas distintas de 28/09 a 09/10/2026. Comparação exata de datas, preços e volumes contra os primeiros dez registros de uma consulta de 15 barras; ordenação crescente somente para exibição. Código executado novamente com credencial comercial em 11/10/2026. Não certifica continuidade de todo o histórico ou ajustes de eventos.
- Teste restrito a TOTS3 e dez registros. Não certifica cobertura de todas as datas, todos os ativos ou todo o histórico.
- OHLC bruto não certifica ajuste por eventos nem retorno total; não é um backtest de patrimônio.
Fontes
- Crivo · histórico OHLC
Fechamentos brutos e cursor UTC; TOTS3 com observações até 09/10/2026. · Consultado em 11 de out. de 2026
- Crivo · documentação e acesso
Autenticação, catálogos, dados HTTP e ferramentas MCP; documentação consultada em 11/10/2026. · Consultado em 11 de out. de 2026