pyield 0.47.0__tar.gz → 0.47.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyield-0.47.0 → pyield-0.47.2}/PKG-INFO +1 -1
- pyield-0.47.2/pyield/__about__.py +1 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/__init__.py +10 -12
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/data_cache.py +2 -6
- pyield-0.47.2/pyield/b3/__init__.py +27 -0
- pyield-0.47.0/pyield/b3/intraday_derivatives.py → pyield-0.47.2/pyield/b3/derivatives_intraday.py +12 -16
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/di1.py +1 -1
- pyield-0.47.2/pyield/b3/futures/__init__.py +153 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/futures/common.py +4 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/futures/historical.py +136 -144
- pyield-0.47.2/pyield/b3/futures/intraday.py +116 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/price_report.py +70 -49
- {pyield-0.47.0 → pyield-0.47.2}/pyield/selic/cpm.py +2 -2
- pyield-0.47.0/pyield/__about__.py +0 -1
- pyield-0.47.0/pyield/b3/__init__.py +0 -15
- pyield-0.47.0/pyield/b3/futures/__init__.py +0 -178
- pyield-0.47.0/pyield/b3/futures/intraday.py +0 -160
- {pyield-0.47.0 → pyield-0.47.2}/.gitignore +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/LICENSE +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/README.md +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/cache.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/converters.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/retry.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/_internal/types.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/ettj_intraday.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/ettj_last.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/ima.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/imaq.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/anbima/tpf.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/_contracts.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/_validar_pregao.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/b3/di_over.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/auction.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/copom.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/ptax_api.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/rates.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/repo.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/trades_intraday.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/trades_monthly.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bc/vna.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bday/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bday/core.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bday/holidays/br_holidays_new.txt +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bday/holidays/br_holidays_old.txt +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/bday/holidays/brholidays.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/clock.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/fwd.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/interpolator.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/ipca/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/ipca/historical.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/ipca/projected.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/py.typed +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/rmd.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/selic/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/selic/probabilities.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/__init__.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/auctions.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/benchmark.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/lft.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ltn.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ntnb.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ntnb1.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ntnbprinc.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ntnc.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/ntnf.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/pre.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyield/tn/utils.py +0 -0
- {pyield-0.47.0 → pyield-0.47.2}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.47.
|
|
3
|
+
Version: 0.47.2
|
|
4
4
|
Summary: A Python library for analysis of fixed income instruments in Brazil
|
|
5
5
|
Project-URL: Homepage, https://github.com/crdcj/PYield
|
|
6
6
|
Project-URL: Documentation, https://crdcj.github.io/PYield
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.47.2"
|
|
@@ -2,7 +2,7 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
from pyield import anbima, b3, bc, bday, ipca, selic, tn
|
|
4
4
|
from pyield.__about__ import __version__
|
|
5
|
-
from pyield.b3 import di1, futures
|
|
5
|
+
from pyield.b3 import di1, futures
|
|
6
6
|
from pyield.clock import now, today
|
|
7
7
|
from pyield.fwd import forward, forwards
|
|
8
8
|
from pyield.interpolator import Interpolator
|
|
@@ -11,33 +11,31 @@ from pyield.selic.cpm import data as copom_options
|
|
|
11
11
|
from pyield.tn import lft, ltn, ntnb, ntnb1, ntnbprinc, ntnc, ntnf, pre
|
|
12
12
|
|
|
13
13
|
__all__ = [
|
|
14
|
+
"Interpolator",
|
|
14
15
|
"__version__",
|
|
15
16
|
"anbima",
|
|
16
|
-
"bc",
|
|
17
17
|
"b3",
|
|
18
|
-
"
|
|
19
|
-
"tn",
|
|
18
|
+
"bc",
|
|
20
19
|
"bday",
|
|
20
|
+
"copom_options",
|
|
21
21
|
"di1",
|
|
22
|
-
"forwards",
|
|
23
22
|
"forward",
|
|
23
|
+
"forwards",
|
|
24
24
|
"futures",
|
|
25
|
-
"copom_options",
|
|
26
|
-
"Interpolator",
|
|
27
|
-
"today",
|
|
28
|
-
"now",
|
|
29
25
|
"lft",
|
|
30
26
|
"ltn",
|
|
27
|
+
"now",
|
|
31
28
|
"ntnb",
|
|
32
|
-
"ntnbprinc",
|
|
33
29
|
"ntnb1",
|
|
30
|
+
"ntnbprinc",
|
|
34
31
|
"ntnc",
|
|
35
32
|
"ntnf",
|
|
33
|
+
"ipca",
|
|
36
34
|
"pre",
|
|
37
35
|
"rmd",
|
|
38
|
-
"bday",
|
|
39
36
|
"selic",
|
|
40
|
-
"
|
|
37
|
+
"tn",
|
|
38
|
+
"today",
|
|
41
39
|
]
|
|
42
40
|
|
|
43
41
|
# Configura o logger do pacote principal com um NullHandler
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import functools
|
|
2
|
-
import io
|
|
3
2
|
import logging
|
|
4
3
|
from enum import Enum
|
|
5
4
|
from typing import Literal
|
|
@@ -55,11 +54,8 @@ def _carregar_arquivo_github(url_arquivo: str) -> pl.DataFrame:
|
|
|
55
54
|
# Garante que a requisição foi sucesso (200 OK), senão levanta erro
|
|
56
55
|
response.raise_for_status()
|
|
57
56
|
|
|
58
|
-
# 2.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# 3. O Polars lê o buffer como se fosse um arquivo local
|
|
62
|
-
return pl.read_parquet(file_buffer)
|
|
57
|
+
# 2. O Polars lê o buffer como se fosse um arquivo local
|
|
58
|
+
return pl.read_parquet(response.content)
|
|
63
59
|
|
|
64
60
|
|
|
65
61
|
@functools.lru_cache(maxsize=8)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from pyield.b3 import di1
|
|
2
|
+
from pyield.b3.derivatives_intraday import derivatives_intraday_fetch
|
|
3
|
+
from pyield.b3.di_over import di_over
|
|
4
|
+
from pyield.b3.futures import (
|
|
5
|
+
futures,
|
|
6
|
+
futures_available_dates,
|
|
7
|
+
futures_enrich,
|
|
8
|
+
futures_intraday,
|
|
9
|
+
)
|
|
10
|
+
from pyield.b3.price_report import (
|
|
11
|
+
price_report_extract,
|
|
12
|
+
price_report_fetch,
|
|
13
|
+
price_report_read,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"di_over",
|
|
18
|
+
"di1",
|
|
19
|
+
"futures",
|
|
20
|
+
"futures_available_dates",
|
|
21
|
+
"futures_enrich",
|
|
22
|
+
"futures_intraday",
|
|
23
|
+
"derivatives_intraday_fetch",
|
|
24
|
+
"price_report_extract",
|
|
25
|
+
"price_report_fetch",
|
|
26
|
+
"price_report_read",
|
|
27
|
+
]
|
pyield-0.47.0/pyield/b3/intraday_derivatives.py → pyield-0.47.2/pyield/b3/derivatives_intraday.py
RENAMED
|
@@ -35,7 +35,6 @@ import requests
|
|
|
35
35
|
from pyield import clock
|
|
36
36
|
from pyield._internal.cache import ttl_cache
|
|
37
37
|
from pyield._internal.retry import retry_padrao
|
|
38
|
-
from pyield.b3._validar_pregao import intraday_disponivel
|
|
39
38
|
|
|
40
39
|
URL_BASE_INTRADAY = "https://cotacao.b3.com.br/mds/api/v1/DerivativeQuotation"
|
|
41
40
|
|
|
@@ -111,7 +110,7 @@ def _processar_colunas_intraday(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
111
110
|
)
|
|
112
111
|
|
|
113
112
|
|
|
114
|
-
def
|
|
113
|
+
def derivatives_intraday_fetch(contract_code: str) -> pl.DataFrame:
|
|
115
114
|
"""Busca cotações intraday brutas de derivativos da B3.
|
|
116
115
|
|
|
117
116
|
Faz a chamada ao endpoint ``DerivativeQuotation`` e devolve um DataFrame
|
|
@@ -123,7 +122,8 @@ def fetch_intraday_derivatives(contract_code: str) -> pl.DataFrame:
|
|
|
123
122
|
derivados devem ser feitos no módulo consumidor.
|
|
124
123
|
|
|
125
124
|
Args:
|
|
126
|
-
contract_code: Código base do derivativo na B3
|
|
125
|
+
contract_code: Código base do derivativo na B3
|
|
126
|
+
(ex.: ``DI1``, ``DOL``, ``DAP``, ``DDI``, ``FRC``, ``FRO``, ``IND``).
|
|
127
127
|
|
|
128
128
|
Returns:
|
|
129
129
|
DataFrame Polars com o payload normalizado da API.
|
|
@@ -150,21 +150,17 @@ def fetch_intraday_derivatives(contract_code: str) -> pl.DataFrame:
|
|
|
150
150
|
* preco_oferta_compra (Float64): melhor oferta de compra (opcional).
|
|
151
151
|
* preco_oferta_venda (Float64): melhor oferta de venda (opcional).
|
|
152
152
|
* tipo_lado (String): tipo de lado (opcional).
|
|
153
|
-
*
|
|
154
|
-
|
|
153
|
+
* horario_referencia (Time): horário aproximado a que os
|
|
154
|
+
dados se referem. A fonte intraday da B3 possui atraso de
|
|
155
|
+
~15 min; este valor é calculado subtraindo esse atraso do
|
|
156
|
+
horário da consulta.
|
|
155
157
|
"""
|
|
156
|
-
if not intraday_disponivel():
|
|
157
|
-
return pl.DataFrame()
|
|
158
|
-
|
|
159
158
|
dados_json = _buscar_json_intraday(contract_code)
|
|
160
159
|
if not dados_json:
|
|
161
160
|
return pl.DataFrame()
|
|
162
161
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
-
.sort("codigo_negociacao")
|
|
170
|
-
)
|
|
162
|
+
df = _converter_json_intraday(dados_json)
|
|
163
|
+
df = _processar_colunas_intraday(df)
|
|
164
|
+
horario = (clock.now() - dt.timedelta(minutes=15)).time()
|
|
165
|
+
df = df.with_columns(horario_referencia=horario)
|
|
166
|
+
return df.sort("codigo_negociacao")
|
|
@@ -4,7 +4,7 @@ import pyield._internal.converters as cv
|
|
|
4
4
|
from pyield import b3, bday, interpolator
|
|
5
5
|
from pyield._internal.data_cache import obter_dataset_cacheado
|
|
6
6
|
from pyield._internal.types import ArrayLike, DateLike, any_is_collection, any_is_empty
|
|
7
|
-
from pyield.b3.futures import
|
|
7
|
+
from pyield.b3.futures import futures_available_dates as _listar_datas
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def data(
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import polars as pl
|
|
2
|
+
|
|
3
|
+
import pyield._internal.converters as cv
|
|
4
|
+
from pyield._internal.types import ArrayLike, DateLike, any_is_empty
|
|
5
|
+
from pyield.b3._validar_pregao import data_negociacao_valida
|
|
6
|
+
from pyield.b3.futures import historical, intraday
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def futures_enrich(
|
|
10
|
+
df: pl.DataFrame,
|
|
11
|
+
contract_code: str,
|
|
12
|
+
) -> pl.DataFrame:
|
|
13
|
+
"""Enriquece DataFrame bruto do Price Report (PR) da B3.
|
|
14
|
+
|
|
15
|
+
Aceita um DataFrame com colunas no schema original da B3
|
|
16
|
+
(ex.: ``TradDt``, ``TckrSymb``) ou já renomeadas para o padrão
|
|
17
|
+
PYield. Adiciona data de vencimento, dias úteis/corridos e
|
|
18
|
+
colunas derivadas (dv01, taxa_forward) conforme o contrato.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
df: DataFrame com dados do PR da B3.
|
|
22
|
+
contract_code: Código do contrato futuro
|
|
23
|
+
(ex.: "DI1", "DOL").
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
DataFrame Polars enriquecido e ordenado.
|
|
27
|
+
"""
|
|
28
|
+
return historical.enrich(df, contract_code)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def futures(
|
|
32
|
+
date: DateLike | ArrayLike,
|
|
33
|
+
contract_code: str,
|
|
34
|
+
) -> pl.DataFrame:
|
|
35
|
+
"""Busca dados de um contrato futuro da B3 para a data de referência.
|
|
36
|
+
|
|
37
|
+
Dados obtidos do dataset PR cacheado no GitHub (disponível desde 2018).
|
|
38
|
+
Para dados do pregão corrente, use ``futures_intraday``.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
date: Data de referência para consulta ou coleção de datas.
|
|
42
|
+
Quando uma coleção é fornecida, os dados são buscados para cada
|
|
43
|
+
data individualmente e concatenados. Datas inválidas (feriados,
|
|
44
|
+
fins de semana, futuras) são silenciosamente ignoradas.
|
|
45
|
+
contract_code: Código do contrato futuro na B3. Contratos
|
|
46
|
+
disponíveis no cache histórico:
|
|
47
|
+
- Juros: DI1, DDI, FRC, FRO, DAP
|
|
48
|
+
- Moedas: DOL, WDO
|
|
49
|
+
- Índices: IND, WIN
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
DataFrame Polars com os dados do contrato informado.
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
>>> df = futures("31-05-2024", "DI1")
|
|
56
|
+
>>> df = futures("31-05-2024", "DAP")
|
|
57
|
+
|
|
58
|
+
Lista de datas:
|
|
59
|
+
|
|
60
|
+
>>> df = futures(["29-05-2024", "31-05-2024"], "DI1")
|
|
61
|
+
>>> df["data_referencia"].unique().sort().to_list()
|
|
62
|
+
[datetime.date(2024, 5, 29), datetime.date(2024, 5, 31)]
|
|
63
|
+
|
|
64
|
+
Véspera de Natal e Ano Novo não têm pregão:
|
|
65
|
+
|
|
66
|
+
>>> futures("24-12-2024", "DI1").is_empty()
|
|
67
|
+
True
|
|
68
|
+
>>> futures("31-12-2024", "DI1").is_empty()
|
|
69
|
+
True
|
|
70
|
+
|
|
71
|
+
Data futura e fim de semana retornam DataFrame vazio:
|
|
72
|
+
|
|
73
|
+
>>> import datetime as dt
|
|
74
|
+
>>> amanha = dt.date.today() + dt.timedelta(days=1)
|
|
75
|
+
>>> futures(amanha, "DI1").is_empty()
|
|
76
|
+
True
|
|
77
|
+
>>> futures("04-01-2025", "DI1").is_empty() # sábado
|
|
78
|
+
True
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
if any_is_empty(date, contract_code):
|
|
82
|
+
return pl.DataFrame()
|
|
83
|
+
|
|
84
|
+
dados_convertidos = cv.converter_datas(date)
|
|
85
|
+
if isinstance(dados_convertidos, pl.Series):
|
|
86
|
+
datas_validas = []
|
|
87
|
+
for d in dados_convertidos:
|
|
88
|
+
if d is not None and data_negociacao_valida(d):
|
|
89
|
+
datas_validas.append(d)
|
|
90
|
+
return historical._buscar_do_cache(datas_validas, contract_code)
|
|
91
|
+
|
|
92
|
+
if not data_negociacao_valida(dados_convertidos):
|
|
93
|
+
return pl.DataFrame()
|
|
94
|
+
|
|
95
|
+
return historical.historical(dados_convertidos, contract_code)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def futures_intraday(
|
|
99
|
+
contract_code: str,
|
|
100
|
+
) -> pl.DataFrame:
|
|
101
|
+
"""Busca dados intraday de contratos futuros da B3.
|
|
102
|
+
|
|
103
|
+
Retorna os dados mais recentes do pregão corrente, com atraso
|
|
104
|
+
aproximado de 15 minutos. Para dados históricos consolidados,
|
|
105
|
+
use ``futures``.
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
contract_code: Código do contrato futuro na B3
|
|
109
|
+
(ex.: 'DI1', 'DAP', 'DOL').
|
|
110
|
+
|
|
111
|
+
Returns:
|
|
112
|
+
DataFrame Polars com dados intraday. Retorna DataFrame vazio
|
|
113
|
+
fora do horário de pregão.
|
|
114
|
+
|
|
115
|
+
Notes:
|
|
116
|
+
As colunas com prefixo ``preco_`` aparecem para contratos cotados
|
|
117
|
+
por preço (ex.: DOL, IND). As com prefixo ``taxa_`` aparecem para
|
|
118
|
+
contratos cotados por taxa (ex.: DI1, DAP, DDI, FRC, FRO).
|
|
119
|
+
"""
|
|
120
|
+
if not contract_code:
|
|
121
|
+
return pl.DataFrame()
|
|
122
|
+
|
|
123
|
+
return intraday.intraday(contract_code)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def futures_available_dates(contract_code: str) -> pl.Series:
|
|
127
|
+
"""Retorna as datas de negociação disponíveis no dataset cacheado.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
contract_code: Código do contrato futuro na B3 (ex.: DI1, DOL).
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Series ordenada de datas (Date) para as quais há dados de ajuste.
|
|
134
|
+
|
|
135
|
+
Examples:
|
|
136
|
+
>>> from pyield.b3.futures import futures_available_dates
|
|
137
|
+
>>> futures_available_dates("DI1").head(3)
|
|
138
|
+
shape: (3,)
|
|
139
|
+
Series: 'data_referencia' [date]
|
|
140
|
+
[
|
|
141
|
+
2018-01-02
|
|
142
|
+
2018-01-03
|
|
143
|
+
2018-01-04
|
|
144
|
+
]
|
|
145
|
+
"""
|
|
146
|
+
return historical.listar_datas_disponiveis(contract_code)
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
__all__ = [
|
|
150
|
+
"futures",
|
|
151
|
+
"futures_available_dates",
|
|
152
|
+
"futures_intraday",
|
|
153
|
+
]
|
|
@@ -2,6 +2,10 @@ import polars as pl
|
|
|
2
2
|
|
|
3
3
|
from pyield import bday
|
|
4
4
|
|
|
5
|
+
# Lista de contratos que negociam por taxa (juros/cupom).
|
|
6
|
+
# Nestes contratos, as colunas OHLC são taxas e precisam ser divididas por 100.
|
|
7
|
+
CONTRATOS_TAXA = {"DI1", "DAP", "DDI", "FRC", "FRO"}
|
|
8
|
+
|
|
5
9
|
_MAPA_MESES: dict[str, int] = {
|
|
6
10
|
"F": 1,
|
|
7
11
|
"G": 2,
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import datetime as dt
|
|
2
2
|
|
|
3
3
|
import polars as pl
|
|
4
|
+
import polars.selectors as cs
|
|
4
5
|
|
|
5
6
|
from pyield import bday
|
|
6
7
|
from pyield._internal.data_cache import obter_dataset_cacheado
|
|
7
|
-
from pyield.b3.
|
|
8
|
-
from pyield.b3.futures.common import adicionar_vencimento, expr_dv01
|
|
8
|
+
from pyield.b3.futures.common import CONTRATOS_TAXA, adicionar_vencimento, expr_dv01
|
|
9
9
|
from pyield.fwd import forwards
|
|
10
10
|
|
|
11
|
-
# Lista de contratos que negociam por taxa (juros/cupom).
|
|
12
|
-
# Nestes contratos, as colunas OHLC são taxas e precisam ser divididas por 100.
|
|
13
|
-
CONTRATOS_TAXA = {"DI1", "DAP", "DDI", "FRC", "FRO"}
|
|
14
|
-
|
|
15
11
|
# Renomeação preco_* → taxa_* para contratos cotados por taxa.
|
|
16
12
|
# Bid/Ask são invertidos: BestBidPric (bid em PU) = menor taxa = venda de taxa;
|
|
17
13
|
# BestAskPric (ask em PU) = maior taxa = compra de taxa.
|
|
@@ -27,6 +23,55 @@ _PRECO_PARA_TAXA = {
|
|
|
27
23
|
"preco_limite_maximo": "taxa_limite_maximo",
|
|
28
24
|
}
|
|
29
25
|
|
|
26
|
+
# Colunas de saída para contratos cotados por preço (DOL, WDO, IND, WIN).
|
|
27
|
+
_COLUNAS_CONTRATO_PRECO = (
|
|
28
|
+
"data_referencia",
|
|
29
|
+
"codigo_negociacao",
|
|
30
|
+
"data_vencimento",
|
|
31
|
+
"dias_uteis",
|
|
32
|
+
"dias_corridos",
|
|
33
|
+
"contratos_abertos",
|
|
34
|
+
"numero_negocios",
|
|
35
|
+
"volume_negociado",
|
|
36
|
+
"volume_financeiro",
|
|
37
|
+
"preco_limite_minimo",
|
|
38
|
+
"preco_limite_maximo",
|
|
39
|
+
"preco_abertura",
|
|
40
|
+
"preco_minimo",
|
|
41
|
+
"preco_maximo",
|
|
42
|
+
"preco_medio",
|
|
43
|
+
"preco_fechamento",
|
|
44
|
+
"preco_ultima_oferta_compra",
|
|
45
|
+
"preco_ultima_oferta_venda",
|
|
46
|
+
"preco_ajuste",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Colunas de saída para contratos cotados por taxa (DI1, DAP, DDI, FRC, FRO).
|
|
50
|
+
_COLUNAS_CONTRATO_TAXA = (
|
|
51
|
+
"data_referencia",
|
|
52
|
+
"codigo_negociacao",
|
|
53
|
+
"data_vencimento",
|
|
54
|
+
"dias_uteis",
|
|
55
|
+
"dias_corridos",
|
|
56
|
+
"dv01",
|
|
57
|
+
"contratos_abertos",
|
|
58
|
+
"numero_negocios",
|
|
59
|
+
"volume_negociado",
|
|
60
|
+
"volume_financeiro",
|
|
61
|
+
"preco_ajuste",
|
|
62
|
+
"taxa_limite_minimo",
|
|
63
|
+
"taxa_limite_maximo",
|
|
64
|
+
"taxa_abertura",
|
|
65
|
+
"taxa_minima",
|
|
66
|
+
"taxa_maxima",
|
|
67
|
+
"taxa_media",
|
|
68
|
+
"taxa_fechamento",
|
|
69
|
+
"taxa_ultima_oferta_venda",
|
|
70
|
+
"taxa_ultima_oferta_compra",
|
|
71
|
+
"taxa_ajuste",
|
|
72
|
+
"taxa_forward",
|
|
73
|
+
)
|
|
74
|
+
|
|
30
75
|
# Normaliza o schema XML bruto da B3 para o padrão de colunas deste módulo.
|
|
31
76
|
_RENOMEAR_COLUNAS_PR = {
|
|
32
77
|
"TradDt": "data_referencia",
|
|
@@ -49,25 +94,95 @@ _RENOMEAR_COLUNAS_PR = {
|
|
|
49
94
|
}
|
|
50
95
|
|
|
51
96
|
|
|
52
|
-
def
|
|
53
|
-
|
|
97
|
+
def _obter_cache_filtrado(codigo_contrato: str) -> pl.DataFrame:
|
|
98
|
+
"""Carrega o dataset PR cacheado e filtra por contrato."""
|
|
99
|
+
return obter_dataset_cacheado("futures").filter(
|
|
100
|
+
pl.col("TckrSymb").str.starts_with(codigo_contrato)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _enriquecer_dados(df: pl.DataFrame, codigo_contrato: str) -> pl.DataFrame:
|
|
105
|
+
df = df.with_columns(
|
|
106
|
+
dias_uteis=bday.count_expr("data_referencia", "data_vencimento"),
|
|
107
|
+
dias_corridos=(
|
|
108
|
+
pl.col("data_vencimento") - pl.col("data_referencia")
|
|
109
|
+
).dt.total_days(),
|
|
110
|
+
).filter(pl.col("dias_corridos") > 0)
|
|
111
|
+
|
|
112
|
+
eh_taxa = codigo_contrato in CONTRATOS_TAXA
|
|
113
|
+
if eh_taxa:
|
|
114
|
+
df = df.rename(_PRECO_PARA_TAXA, strict=False)
|
|
115
|
+
df = df.with_columns(cs.starts_with("taxa_").truediv(100).round(6))
|
|
116
|
+
|
|
117
|
+
if codigo_contrato == "DI1":
|
|
118
|
+
df = df.with_columns(
|
|
119
|
+
dv01=expr_dv01("dias_uteis", "taxa_ajuste", "preco_ajuste")
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
if codigo_contrato in {"DI1", "DAP"}:
|
|
123
|
+
df = df.with_columns(
|
|
124
|
+
taxa_forward=forwards(bdays=df["dias_uteis"], rates=df["taxa_ajuste"])
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return df
|
|
54
128
|
|
|
55
129
|
|
|
56
|
-
def
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
130
|
+
def _selecionar_colunas_saida(df: pl.DataFrame, codigo_contrato: str) -> pl.DataFrame:
|
|
131
|
+
if codigo_contrato in CONTRATOS_TAXA:
|
|
132
|
+
colunas = _COLUNAS_CONTRATO_TAXA
|
|
133
|
+
else:
|
|
134
|
+
colunas = _COLUNAS_CONTRATO_PRECO
|
|
135
|
+
return df.select(c for c in colunas if c in df.columns)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _buscar_do_cache(datas: list[dt.date], codigo_contrato: str) -> pl.DataFrame:
|
|
139
|
+
"""Carrega histórico de futuros do dataset PR para uma lista de datas."""
|
|
140
|
+
if not datas:
|
|
141
|
+
return pl.DataFrame()
|
|
142
|
+
|
|
143
|
+
df = _obter_cache_filtrado(codigo_contrato)
|
|
144
|
+
df = df.filter(pl.col("TradDt").is_in(datas))
|
|
145
|
+
if df.is_empty():
|
|
146
|
+
return pl.DataFrame()
|
|
147
|
+
|
|
148
|
+
return enrich(df, codigo_contrato)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def enrich(df: pl.DataFrame, codigo_contrato: str) -> pl.DataFrame:
|
|
152
|
+
"""Enriquece DataFrame bruto do Price Report (PR) da B3.
|
|
153
|
+
|
|
154
|
+
Aceita um DataFrame com colunas no schema original da B3 (ex.:
|
|
155
|
+
``TradDt``, ``TckrSymb``) ou já renomeadas para o padrão PYield.
|
|
156
|
+
Adiciona data de vencimento, dias úteis/corridos e colunas derivadas
|
|
157
|
+
(dv01, taxa_forward) conforme o contrato.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
df: DataFrame com dados do PR da B3.
|
|
161
|
+
codigo_contrato: Código do contrato futuro (ex.: "DI1", "DOL").
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
DataFrame Polars enriquecido e ordenado.
|
|
165
|
+
"""
|
|
166
|
+
if df.is_empty():
|
|
167
|
+
return pl.DataFrame()
|
|
168
|
+
|
|
169
|
+
df = df.rename(_RENOMEAR_COLUNAS_PR)
|
|
170
|
+
df = adicionar_vencimento(df, codigo_contrato, coluna_ticker="codigo_negociacao")
|
|
171
|
+
df = _enriquecer_dados(df, codigo_contrato)
|
|
172
|
+
df = _selecionar_colunas_saida(df, codigo_contrato)
|
|
173
|
+
|
|
174
|
+
return df.sort("data_referencia", "data_vencimento")
|
|
60
175
|
|
|
61
176
|
|
|
62
177
|
def historical(
|
|
63
178
|
data: dt.date,
|
|
64
|
-
codigo_contrato: str
|
|
179
|
+
codigo_contrato: str,
|
|
65
180
|
) -> pl.DataFrame:
|
|
66
181
|
"""Busca histórico de futuros no dataset PR cacheado.
|
|
67
182
|
|
|
68
183
|
Args:
|
|
69
184
|
data: Data de negociação.
|
|
70
|
-
codigo_contrato: Código
|
|
185
|
+
codigo_contrato: Código do contrato futuro na B3.
|
|
71
186
|
|
|
72
187
|
Returns:
|
|
73
188
|
DataFrame Polars com dados históricos de futuros.
|
|
@@ -113,10 +228,10 @@ def historical(
|
|
|
113
228
|
Usa exclusivamente o dataset PR cacheado no GitHub. Contratos
|
|
114
229
|
disponíveis: DI1, DDI, FRC, FRO, DAP, DOL, WDO, IND, WIN.
|
|
115
230
|
|
|
116
|
-
As colunas com prefixo ``preco_`` aparecem para contratos cotados
|
|
117
|
-
preço (ex.: DOL, IND). As com prefixo ``taxa_`` aparecem para
|
|
118
|
-
cotados por taxa (ex.: DI1, DAP, DDI, FRC, FRO). Nem
|
|
119
|
-
estarão presentes em todos os contratos.
|
|
231
|
+
As colunas com prefixo ``preco_`` aparecem para contratos cotados
|
|
232
|
+
por preço (ex.: DOL, IND). As com prefixo ``taxa_`` aparecem para
|
|
233
|
+
contratos cotados por taxa (ex.: DI1, DAP, DDI, FRC, FRO). Nem
|
|
234
|
+
todas as colunas estarão presentes em todos os contratos.
|
|
120
235
|
|
|
121
236
|
``*_fechamento`` é o último negócio realizado (last trade).
|
|
122
237
|
``*_ultima_oferta_*`` é o bid/ask ao fim do pregão — não
|
|
@@ -125,139 +240,16 @@ def historical(
|
|
|
125
240
|
``taxa_ultima_oferta_compra`` = maior taxa (ask em PU),
|
|
126
241
|
``taxa_ultima_oferta_venda`` = menor taxa (bid em PU).
|
|
127
242
|
"""
|
|
128
|
-
|
|
129
|
-
if not codigos:
|
|
130
|
-
return pl.DataFrame()
|
|
131
|
-
|
|
132
|
-
dataframes = [
|
|
133
|
-
df
|
|
134
|
-
for codigo in codigos
|
|
135
|
-
if not (df := _obter_futuros_pr([data], codigo)).is_empty()
|
|
136
|
-
]
|
|
137
|
-
|
|
138
|
-
if not dataframes:
|
|
139
|
-
return pl.DataFrame()
|
|
140
|
-
|
|
141
|
-
if len(dataframes) == 1:
|
|
142
|
-
return dataframes[0]
|
|
143
|
-
|
|
144
|
-
df_resultado = pl.concat(dataframes, how="diagonal_relaxed")
|
|
145
|
-
colunas_ordenacao = [
|
|
146
|
-
coluna
|
|
147
|
-
for coluna in ["data_referencia", "codigo_negociacao", "data_vencimento"]
|
|
148
|
-
if coluna in df_resultado.columns
|
|
149
|
-
]
|
|
150
|
-
if not colunas_ordenacao:
|
|
151
|
-
return df_resultado
|
|
152
|
-
|
|
153
|
-
return df_resultado.sort(*colunas_ordenacao)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
def _obter_futuros_pr(datas: list[dt.date], codigo_contrato: str) -> pl.DataFrame:
|
|
157
|
-
"""Carrega histórico de futuros do dataset PR para uma lista de datas."""
|
|
158
|
-
if not datas:
|
|
159
|
-
return pl.DataFrame()
|
|
160
|
-
|
|
161
|
-
df = _obter_pr_normalizado()
|
|
162
|
-
df = _filtrar_e_renomear(df, datas, codigo_contrato)
|
|
163
|
-
if df.is_empty():
|
|
164
|
-
return pl.DataFrame()
|
|
165
|
-
|
|
166
|
-
df = adicionar_vencimento(df, codigo_contrato, coluna_ticker="codigo_negociacao")
|
|
167
|
-
df = _enriquecer_dados(df, codigo_contrato)
|
|
168
|
-
df = _selecionar_colunas_saida(df)
|
|
169
|
-
|
|
170
|
-
return df.sort("data_referencia", "data_vencimento")
|
|
243
|
+
return _buscar_do_cache([data], codigo_contrato)
|
|
171
244
|
|
|
172
245
|
|
|
173
246
|
def listar_datas_disponiveis(codigo_contrato: str) -> pl.Series:
|
|
174
247
|
"""Lista datas disponíveis no dataset PR para um contrato futuro."""
|
|
175
248
|
return (
|
|
176
|
-
|
|
177
|
-
.
|
|
178
|
-
.get_column("data_referencia")
|
|
249
|
+
_obter_cache_filtrado(codigo_contrato)
|
|
250
|
+
.get_column("TradDt")
|
|
179
251
|
.drop_nulls()
|
|
180
252
|
.unique()
|
|
181
253
|
.sort()
|
|
182
254
|
.alias("data_referencia")
|
|
183
255
|
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
def _filtrar_e_renomear(
|
|
187
|
-
df: pl.DataFrame, datas: list[dt.date], codigo_contrato: str
|
|
188
|
-
) -> pl.DataFrame:
|
|
189
|
-
return df.filter(
|
|
190
|
-
pl.col("data_referencia").is_in(datas),
|
|
191
|
-
pl.col("codigo_negociacao").str.starts_with(codigo_contrato),
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
def _enriquecer_dados(df: pl.DataFrame, codigo_contrato: str) -> pl.DataFrame:
|
|
196
|
-
df = df.with_columns(
|
|
197
|
-
dias_uteis=bday.count_expr("data_referencia", "data_vencimento"),
|
|
198
|
-
dias_corridos=(
|
|
199
|
-
pl.col("data_vencimento") - pl.col("data_referencia")
|
|
200
|
-
).dt.total_days(),
|
|
201
|
-
).filter(pl.col("dias_corridos") > 0)
|
|
202
|
-
|
|
203
|
-
eh_taxa = codigo_contrato in CONTRATOS_TAXA
|
|
204
|
-
if eh_taxa:
|
|
205
|
-
df = df.rename({k: v for k, v in _PRECO_PARA_TAXA.items() if k in df.columns})
|
|
206
|
-
|
|
207
|
-
if eh_taxa:
|
|
208
|
-
colunas_taxa = [c for c in df.columns if c.startswith("taxa_")]
|
|
209
|
-
df = df.with_columns(pl.col(colunas_taxa).truediv(100).round(6))
|
|
210
|
-
|
|
211
|
-
if (
|
|
212
|
-
codigo_contrato == "DI1"
|
|
213
|
-
and "preco_ajuste" in df.columns
|
|
214
|
-
and "taxa_ajuste" in df.columns
|
|
215
|
-
):
|
|
216
|
-
df = df.with_columns(
|
|
217
|
-
dv01=expr_dv01("dias_uteis", "taxa_ajuste", "preco_ajuste")
|
|
218
|
-
)
|
|
219
|
-
|
|
220
|
-
if codigo_contrato in {"DI1", "DAP"} and "taxa_ajuste" in df.columns:
|
|
221
|
-
df = df.with_columns(
|
|
222
|
-
taxa_forward=forwards(bdays=df["dias_uteis"], rates=df["taxa_ajuste"])
|
|
223
|
-
)
|
|
224
|
-
|
|
225
|
-
return df
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
def _selecionar_colunas_saida(df: pl.DataFrame) -> pl.DataFrame:
|
|
229
|
-
ordem_preferida = [
|
|
230
|
-
"data_referencia",
|
|
231
|
-
"codigo_negociacao",
|
|
232
|
-
"data_vencimento",
|
|
233
|
-
"dias_uteis",
|
|
234
|
-
"dias_corridos",
|
|
235
|
-
"dv01",
|
|
236
|
-
"contratos_abertos",
|
|
237
|
-
"numero_negocios",
|
|
238
|
-
"volume_negociado",
|
|
239
|
-
"volume_financeiro",
|
|
240
|
-
"preco_limite_minimo",
|
|
241
|
-
"preco_limite_maximo",
|
|
242
|
-
"preco_abertura",
|
|
243
|
-
"preco_minimo",
|
|
244
|
-
"preco_maximo",
|
|
245
|
-
"preco_medio",
|
|
246
|
-
"preco_fechamento",
|
|
247
|
-
"preco_ultima_oferta_compra",
|
|
248
|
-
"preco_ultima_oferta_venda",
|
|
249
|
-
"preco_ajuste",
|
|
250
|
-
"taxa_limite_minimo",
|
|
251
|
-
"taxa_limite_maximo",
|
|
252
|
-
"taxa_abertura",
|
|
253
|
-
"taxa_minima",
|
|
254
|
-
"taxa_maxima",
|
|
255
|
-
"taxa_media",
|
|
256
|
-
"taxa_fechamento",
|
|
257
|
-
"taxa_ultima_oferta_venda",
|
|
258
|
-
"taxa_ultima_oferta_compra",
|
|
259
|
-
"taxa_ajuste",
|
|
260
|
-
"taxa_forward",
|
|
261
|
-
]
|
|
262
|
-
colunas_existentes = [c for c in ordem_preferida if c in df.columns]
|
|
263
|
-
return df.select(colunas_existentes)
|