PYield 0.46.3__tar.gz → 0.46.4__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.46.3 → pyield-0.46.4}/PKG-INFO +4 -4
- {pyield-0.46.3 → pyield-0.46.4}/README.md +3 -3
- pyield-0.46.4/pyield/__about__.py +1 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/data_cache.py +4 -4
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/_validar_pregao.py +16 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/di1.py +46 -42
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/futures/__init__.py +5 -5
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/futures/common.py +6 -6
- pyield-0.46.4/pyield/b3/futures/historical.py +305 -0
- pyield-0.46.4/pyield/b3/futures/intraday.py +159 -0
- pyield-0.46.4/pyield/b3/intraday_derivatives.py +141 -0
- pyield-0.46.4/pyield/b3/price_report.py +381 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/selic/cpm.py +73 -78
- {pyield-0.46.3 → pyield-0.46.4}/pyield/selic/probabilities.py +64 -64
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ltn.py +1 -1
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ntnb.py +15 -7
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ntnf.py +5 -5
- pyield-0.46.3/pyield/__about__.py +0 -1
- pyield-0.46.3/pyield/b3/futures/historical.py +0 -247
- pyield-0.46.3/pyield/b3/futures/intraday.py +0 -173
- pyield-0.46.3/pyield/b3/intraday_derivatives.py +0 -132
- pyield-0.46.3/pyield/b3/price_report.py +0 -439
- {pyield-0.46.3 → pyield-0.46.4}/.gitignore +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/LICENSE +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/br_numbers.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/cache.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/converters.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/retry.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/_internal/types.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/difusao.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/ettj_intraday.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/ettj_last.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/ima.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/imaq.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/anbima/tpf.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/_contracts.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/b3/di_over.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/auction.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/copom.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/ptax_api.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/rates.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/repo.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/trades_intraday.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/trades_monthly.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bc/vna.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bday/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bday/core.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bday/holidays/br_holidays_new.txt +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bday/holidays/br_holidays_old.txt +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/bday/holidays/brholidays.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/clock.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/fwd.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/interpolator.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/ipca/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/ipca/historical.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/ipca/projected.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/py.typed +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/rmd.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/selic/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/__init__.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/auctions.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/benchmark.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/lft.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ntnb1.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ntnbprinc.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/ntnc.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/pre.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyield/tn/utils.py +0 -0
- {pyield-0.46.3 → pyield-0.46.4}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.46.
|
|
3
|
+
Version: 0.46.4
|
|
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
|
|
@@ -71,10 +71,10 @@ yd.bday.offset("29-12-2023", 1) # -> datetime.date(2024, 1, 2)
|
|
|
71
71
|
|
|
72
72
|
# Curva de DI Futuro
|
|
73
73
|
df = yd.futures("31-05-2024", "DI1")
|
|
74
|
-
# Columns:
|
|
74
|
+
# Columns: data_referencia, codigo_negociacao, data_vencimento, dias_uteis, taxa_ajuste, ...
|
|
75
75
|
|
|
76
76
|
# Interpolação de taxas (flat forward, convenção 252 dias úteis/ano)
|
|
77
|
-
interp = yd.Interpolator("flat_forward", df["
|
|
77
|
+
interp = yd.Interpolator("flat_forward", df["dias_uteis"], df["taxa_ajuste"])
|
|
78
78
|
interp(45) # -> 0.04833...
|
|
79
79
|
interp([30, 60]) # -> pl.Series with interpolated rates
|
|
80
80
|
|
|
@@ -200,7 +200,7 @@ ntnb.quotation("31-05-2024", "15-08-2060", 0.061878) # -> 99.5341
|
|
|
200
200
|
|
|
201
201
|
# Spreads de DI (bps=True multiplica por 10.000)
|
|
202
202
|
ntnf.di_spreads("30-05-2025", bps=True)
|
|
203
|
-
# -> DataFrame:
|
|
203
|
+
# -> DataFrame: titulo, data_vencimento, spread_di
|
|
204
204
|
```
|
|
205
205
|
|
|
206
206
|
## Dados de Futuros
|
|
@@ -27,10 +27,10 @@ yd.bday.offset("29-12-2023", 1) # -> datetime.date(2024, 1, 2)
|
|
|
27
27
|
|
|
28
28
|
# Curva de DI Futuro
|
|
29
29
|
df = yd.futures("31-05-2024", "DI1")
|
|
30
|
-
# Columns:
|
|
30
|
+
# Columns: data_referencia, codigo_negociacao, data_vencimento, dias_uteis, taxa_ajuste, ...
|
|
31
31
|
|
|
32
32
|
# Interpolação de taxas (flat forward, convenção 252 dias úteis/ano)
|
|
33
|
-
interp = yd.Interpolator("flat_forward", df["
|
|
33
|
+
interp = yd.Interpolator("flat_forward", df["dias_uteis"], df["taxa_ajuste"])
|
|
34
34
|
interp(45) # -> 0.04833...
|
|
35
35
|
interp([30, 60]) # -> pl.Series with interpolated rates
|
|
36
36
|
|
|
@@ -156,7 +156,7 @@ ntnb.quotation("31-05-2024", "15-08-2060", 0.061878) # -> 99.5341
|
|
|
156
156
|
|
|
157
157
|
# Spreads de DI (bps=True multiplica por 10.000)
|
|
158
158
|
ntnf.di_spreads("30-05-2025", bps=True)
|
|
159
|
-
# -> DataFrame:
|
|
159
|
+
# -> DataFrame: titulo, data_vencimento, spread_di
|
|
160
160
|
```
|
|
161
161
|
|
|
162
162
|
## Dados de Futuros
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.46.4"
|
|
@@ -17,7 +17,7 @@ registro = logging.getLogger(__name__)
|
|
|
17
17
|
# Estrutura interna única — usuário não vê isso
|
|
18
18
|
class _Dataset(Enum):
|
|
19
19
|
TPF = ("anbima_tpf.parquet", "data_referencia", "TPF (ANBIMA)")
|
|
20
|
-
|
|
20
|
+
FUTURES = ("b3_futures.parquet", "data_referencia", "Futures (B3)")
|
|
21
21
|
|
|
22
22
|
def __init__(self, nome_arquivo: str, coluna_data: str, descricao: str):
|
|
23
23
|
self.nome_arquivo = nome_arquivo
|
|
@@ -25,7 +25,7 @@ class _Dataset(Enum):
|
|
|
25
25
|
self.descricao = descricao
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
type IdDataset = Literal["tpf", "
|
|
28
|
+
type IdDataset = Literal["tpf", "futures"]
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
def _obter_chave_data_hoje() -> str:
|
|
@@ -37,7 +37,7 @@ def _validar_id_dataset(id_dataset: str) -> _Dataset:
|
|
|
37
37
|
try:
|
|
38
38
|
return _Dataset[dataset_normalizado.upper()]
|
|
39
39
|
except KeyError as e:
|
|
40
|
-
msg = f"id_dataset inválido: '{id_dataset}'. Valores aceitos: 'tpf', '
|
|
40
|
+
msg = f"id_dataset inválido: '{id_dataset}'. Valores aceitos: 'tpf', 'futures'."
|
|
41
41
|
raise ValueError(msg) from e
|
|
42
42
|
|
|
43
43
|
|
|
@@ -81,7 +81,7 @@ def obter_dataset_cacheado(id_dataset: IdDataset) -> pl.DataFrame:
|
|
|
81
81
|
Obtém um dataset pelo ID. Cache expira diariamente.
|
|
82
82
|
|
|
83
83
|
Args:
|
|
84
|
-
id_dataset: "tpf" ou "
|
|
84
|
+
id_dataset: "tpf" ou "futures"
|
|
85
85
|
"""
|
|
86
86
|
df = _obter_dataset_com_ttl(id_dataset.lower(), _obter_chave_data_hoje())
|
|
87
87
|
return df.clone()
|
|
@@ -2,6 +2,10 @@ import datetime as dt
|
|
|
2
2
|
|
|
3
3
|
from pyield import bday, clock
|
|
4
4
|
|
|
5
|
+
# Pregão abre às 9:00, porém os dados intraday têm atraso de 15 minutos.
|
|
6
|
+
# Esperar 1 minuto adicional para garantir que estejam disponíveis (9:16h).
|
|
7
|
+
HORA_INICIO_INTRADAY = dt.time(9, 16)
|
|
8
|
+
|
|
5
9
|
|
|
6
10
|
def data_negociacao_valida(data_negociacao: dt.date) -> bool:
|
|
7
11
|
"""Valida se a data de referência é utilizável para consulta.
|
|
@@ -26,3 +30,15 @@ def data_negociacao_valida(data_negociacao: dt.date) -> bool:
|
|
|
26
30
|
return False
|
|
27
31
|
|
|
28
32
|
return True
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def intraday_disponivel() -> bool:
|
|
36
|
+
"""Verifica se dados intraday estão disponíveis agora.
|
|
37
|
+
|
|
38
|
+
Critérios:
|
|
39
|
+
- Hoje deve ser um dia de pregão válido.
|
|
40
|
+
- O horário atual deve ser após o início dos dados intraday (9:16h).
|
|
41
|
+
"""
|
|
42
|
+
if not data_negociacao_valida(clock.today()):
|
|
43
|
+
return False
|
|
44
|
+
return clock.now().time() >= HORA_INICIO_INTRADAY
|
|
@@ -36,12 +36,19 @@ def data(
|
|
|
36
36
|
Examples:
|
|
37
37
|
>>> from pyield import di1
|
|
38
38
|
>>> df = di1.data(dates="16-10-2024", month_start=True)
|
|
39
|
-
>>>
|
|
40
|
-
...
|
|
39
|
+
>>> df.head(3).select(
|
|
40
|
+
... "codigo_negociacao", "data_vencimento", "dias_uteis", "taxa_ajuste"
|
|
41
41
|
... )
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
42
|
+
shape: (3, 4)
|
|
43
|
+
┌───────────────────┬─────────────────┬────────────┬─────────────┐
|
|
44
|
+
│ codigo_negociacao ┆ data_vencimento ┆ dias_uteis ┆ taxa_ajuste │
|
|
45
|
+
│ --- ┆ --- ┆ --- ┆ --- │
|
|
46
|
+
│ str ┆ date ┆ i64 ┆ f64 │
|
|
47
|
+
╞═══════════════════╪═════════════════╪════════════╪═════════════╡
|
|
48
|
+
│ DI1X24 ┆ 2024-11-01 ┆ 12 ┆ 0.10653 │
|
|
49
|
+
│ DI1Z24 ┆ 2024-12-01 ┆ 31 ┆ 0.1091 │
|
|
50
|
+
│ DI1F25 ┆ 2025-01-01 ┆ 52 ┆ 0.11164 │
|
|
51
|
+
└───────────────────┴─────────────────┴────────────┴─────────────┘
|
|
45
52
|
|
|
46
53
|
"""
|
|
47
54
|
if any_is_empty(dates):
|
|
@@ -65,33 +72,29 @@ def data(
|
|
|
65
72
|
.filter(pl.col("titulo").is_in(["LTN", "NTN-F"]))
|
|
66
73
|
.unique(subset=["data_vencimento", "data_referencia"])
|
|
67
74
|
.select(
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
data_ref_tpf=pl.col("data_referencia"),
|
|
76
|
+
data_vencimento=bday.offset_expr("data_vencimento", 0),
|
|
70
77
|
)
|
|
71
|
-
.sort("
|
|
78
|
+
.sort("data_ref_tpf", "data_vencimento")
|
|
72
79
|
)
|
|
73
80
|
|
|
74
|
-
#
|
|
75
|
-
# exp_dates = bday.offset(df_tpf["ExpirationDate"], 0)
|
|
76
|
-
# df_tpf = df_tpf.with_columns(ExpirationDate=exp_dates)
|
|
77
|
-
|
|
78
|
-
# Mapear cada TradeDate do DI para a data TPF mais próxima
|
|
81
|
+
# Mapear cada data_referencia do DI para a data TPF mais próxima
|
|
79
82
|
df = df.join_asof(
|
|
80
|
-
df_tpf.select("
|
|
81
|
-
left_on="
|
|
82
|
-
right_on="
|
|
83
|
+
df_tpf.select("data_ref_tpf").unique().sort("data_ref_tpf"),
|
|
84
|
+
left_on="data_referencia",
|
|
85
|
+
right_on="data_ref_tpf",
|
|
83
86
|
strategy="backward",
|
|
84
87
|
)
|
|
85
88
|
|
|
86
89
|
# Filtrar apenas vencimentos que existem no TPF
|
|
87
|
-
df = df.join(df_tpf, on=["
|
|
88
|
-
"
|
|
90
|
+
df = df.join(df_tpf, on=["data_ref_tpf", "data_vencimento"], how="inner").drop(
|
|
91
|
+
"data_ref_tpf"
|
|
89
92
|
)
|
|
90
93
|
|
|
91
94
|
if month_start:
|
|
92
|
-
df = df.with_columns(pl.col("
|
|
95
|
+
df = df.with_columns(pl.col("data_vencimento").dt.truncate("1mo"))
|
|
93
96
|
|
|
94
|
-
return df.sort("
|
|
97
|
+
return df.sort("data_referencia", "data_vencimento")
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
def interpolate_rates(
|
|
@@ -145,7 +148,7 @@ def interpolate_rates(
|
|
|
145
148
|
... expirations=["01-01-2027", "25-11-2027", "01-01-2030"],
|
|
146
149
|
... )
|
|
147
150
|
shape: (3,)
|
|
148
|
-
Series: '
|
|
151
|
+
Series: 'taxa_interpolada' [f64]
|
|
149
152
|
[
|
|
150
153
|
0.13972
|
|
151
154
|
0.134613
|
|
@@ -159,7 +162,7 @@ def interpolate_rates(
|
|
|
159
162
|
... extrapolate=True,
|
|
160
163
|
... )
|
|
161
164
|
shape: (2,)
|
|
162
|
-
Series: '
|
|
165
|
+
Series: 'taxa_interpolada' [f64]
|
|
163
166
|
[
|
|
164
167
|
0.13901
|
|
165
168
|
0.13881
|
|
@@ -172,7 +175,7 @@ def interpolate_rates(
|
|
|
172
175
|
... extrapolate=False,
|
|
173
176
|
... )
|
|
174
177
|
shape: (2,)
|
|
175
|
-
Series: '
|
|
178
|
+
Series: 'taxa_interpolada' [f64]
|
|
176
179
|
[
|
|
177
180
|
0.135763
|
|
178
181
|
null
|
|
@@ -182,16 +185,16 @@ def interpolate_rates(
|
|
|
182
185
|
return pl.Series(dtype=pl.Float64)
|
|
183
186
|
|
|
184
187
|
df_entrada = pl.DataFrame(
|
|
185
|
-
data={"
|
|
188
|
+
data={"data_referencia": dates, "data_vencimento": expirations}
|
|
186
189
|
).with_columns(
|
|
187
|
-
|
|
188
|
-
|
|
190
|
+
data_referencia=cv.converter_datas_expr("data_referencia"),
|
|
191
|
+
data_vencimento=cv.converter_datas_expr("data_vencimento"),
|
|
189
192
|
)
|
|
190
193
|
if df_entrada.is_empty():
|
|
191
194
|
return pl.Series(dtype=pl.Float64)
|
|
192
195
|
|
|
193
196
|
# Carrega dataset de taxas DI usando datas já convertidas do df_entrada
|
|
194
|
-
datas_unicas = df_entrada["
|
|
197
|
+
datas_unicas = df_entrada["data_referencia"].drop_nulls().unique().sort().to_list()
|
|
195
198
|
df_ref = b3.futures(date=datas_unicas, contract_code="DI1", full_report=False)
|
|
196
199
|
# Retorna Series vazia se nenhuma taxa for encontrada
|
|
197
200
|
if df_ref.is_empty():
|
|
@@ -201,22 +204,22 @@ def interpolate_rates(
|
|
|
201
204
|
# Isso garante que saberemos a ordem exata depois
|
|
202
205
|
df_entrada = df_entrada.with_row_index("_temp_idx")
|
|
203
206
|
|
|
204
|
-
# Inicializa
|
|
207
|
+
# Inicializa taxa_interpolada como None
|
|
205
208
|
df_entrada = df_entrada.with_columns(
|
|
206
|
-
|
|
207
|
-
|
|
209
|
+
dias_uteis=bday.count_expr("data_referencia", "data_vencimento"),
|
|
210
|
+
taxa_interpolada=None,
|
|
208
211
|
)
|
|
209
212
|
|
|
210
213
|
# Lista para armazenar os blocos processados
|
|
211
214
|
blocos_processados = []
|
|
212
215
|
|
|
213
216
|
# Itera sobre cada data de referência única
|
|
214
|
-
for data_ref in df_entrada["
|
|
217
|
+
for data_ref in df_entrada["data_referencia"].unique():
|
|
215
218
|
# 1. Filtra apenas as linhas desta data (Particionamento)
|
|
216
|
-
df_parcial = df_entrada.filter(pl.col("
|
|
219
|
+
df_parcial = df_entrada.filter(pl.col("data_referencia") == data_ref)
|
|
217
220
|
|
|
218
221
|
# 2. Busca as taxas de referência para esta data
|
|
219
|
-
df_referencia = df_ref.filter(pl.col("
|
|
222
|
+
df_referencia = df_ref.filter(pl.col("data_referencia") == data_ref)
|
|
220
223
|
|
|
221
224
|
# Se não houver dados de curva, adicionamos o bloco como está (com nulos)
|
|
222
225
|
# e continuamos.
|
|
@@ -227,17 +230,17 @@ def interpolate_rates(
|
|
|
227
230
|
# Inicializa o interpolador com taxas e dias úteis conhecidos
|
|
228
231
|
interpolador = interpolator.Interpolator(
|
|
229
232
|
method="flat_forward",
|
|
230
|
-
known_bdays=df_referencia["
|
|
231
|
-
known_rates=df_referencia["
|
|
233
|
+
known_bdays=df_referencia["dias_uteis"],
|
|
234
|
+
known_rates=df_referencia["taxa_ajuste"],
|
|
232
235
|
extrapolate=extrapolate,
|
|
233
236
|
)
|
|
234
237
|
|
|
235
238
|
# 4. A mágica: map_batches passa a Series inteira para o interpolador
|
|
236
239
|
# O interpolador retorna uma Series, que o Polars alinha perfeitamente
|
|
237
240
|
df_parcial = df_parcial.with_columns(
|
|
238
|
-
pl.col("
|
|
241
|
+
pl.col("dias_uteis")
|
|
239
242
|
.map_batches(interpolador) # Passa Series -> Recebe Series
|
|
240
|
-
.alias("
|
|
243
|
+
.alias("taxa_interpolada")
|
|
241
244
|
)
|
|
242
245
|
|
|
243
246
|
blocos_processados.append(df_parcial)
|
|
@@ -249,7 +252,7 @@ def interpolate_rates(
|
|
|
249
252
|
# O sort("_temp_idx") restaura a ordem original dos inputs
|
|
250
253
|
df_saida = pl.concat(blocos_processados).sort("_temp_idx")
|
|
251
254
|
|
|
252
|
-
return df_saida["
|
|
255
|
+
return df_saida["taxa_interpolada"].fill_nan(None)
|
|
253
256
|
|
|
254
257
|
|
|
255
258
|
def interpolate_rate(
|
|
@@ -315,9 +318,10 @@ def interpolate_rate(
|
|
|
315
318
|
def available_dates() -> pl.Series:
|
|
316
319
|
"""Retorna as datas de negociação disponíveis para DI1.
|
|
317
320
|
|
|
318
|
-
Obtém valores distintos de '
|
|
319
|
-
PR da B3 (mesmo utilizado por `futures`). Inclui apenas datas com
|
|
320
|
-
de ajuste já definidos; o pregão do dia corrente não está
|
|
321
|
+
Obtém valores distintos de 'data_referencia' para DI1, com base no dataset
|
|
322
|
+
histórico PR da B3 (mesmo utilizado por `futures`). Inclui apenas datas com
|
|
323
|
+
preço e taxa de ajuste já definidos; o pregão do dia corrente não está
|
|
324
|
+
incluído.
|
|
321
325
|
|
|
322
326
|
Returns:
|
|
323
327
|
Series ordenada de datas de negociação (dt.date) para as quais dados de
|
|
@@ -328,7 +332,7 @@ def available_dates() -> pl.Series:
|
|
|
328
332
|
>>> # Série disponível no dataset PR começa em 2018-01-02
|
|
329
333
|
>>> di1.available_dates().head(5)
|
|
330
334
|
shape: (5,)
|
|
331
|
-
Series: '
|
|
335
|
+
Series: 'data_referencia' [date]
|
|
332
336
|
[
|
|
333
337
|
2018-01-02
|
|
334
338
|
2018-01-03
|
|
@@ -7,7 +7,7 @@ import pyield._internal.converters as cv
|
|
|
7
7
|
from pyield import clock
|
|
8
8
|
from pyield._internal.types import ArrayLike, DateLike, any_is_empty, is_collection
|
|
9
9
|
from pyield.b3._contracts import normalizar_codigos_contrato
|
|
10
|
-
from pyield.b3._validar_pregao import data_negociacao_valida
|
|
10
|
+
from pyield.b3._validar_pregao import HORA_INICIO_INTRADAY, data_negociacao_valida
|
|
11
11
|
from pyield.b3.futures import historical, intraday
|
|
12
12
|
|
|
13
13
|
# A partir desse horário, os dados consolidados (SPR/PR) já estão disponíveis.
|
|
@@ -58,7 +58,7 @@ def futures(
|
|
|
58
58
|
Lista de datas:
|
|
59
59
|
|
|
60
60
|
>>> df = futures(["29-05-2024", "31-05-2024"], "DI1")
|
|
61
|
-
>>> df["
|
|
61
|
+
>>> df["data_referencia"].unique().sort().to_list()
|
|
62
62
|
[datetime.date(2024, 5, 29), datetime.date(2024, 5, 31)]
|
|
63
63
|
|
|
64
64
|
Véspera de Natal e Ano Novo não têm pregão:
|
|
@@ -125,7 +125,7 @@ def _buscar_varias_datas(
|
|
|
125
125
|
# Identifica datas que não vieram do cache
|
|
126
126
|
datas_no_cache: set[dt.date] = set()
|
|
127
127
|
for df in resultados:
|
|
128
|
-
datas_no_cache.update(df["
|
|
128
|
+
datas_no_cache.update(df["data_referencia"].unique().to_list())
|
|
129
129
|
datas_faltantes = [d for d in datas_validas if d not in datas_no_cache]
|
|
130
130
|
|
|
131
131
|
# Fallback individual apenas para datas faltantes
|
|
@@ -156,7 +156,7 @@ def _buscar_por_fonte(
|
|
|
156
156
|
return df
|
|
157
157
|
return intraday.intraday(codigos)
|
|
158
158
|
|
|
159
|
-
if horario >=
|
|
159
|
+
if horario >= HORA_INICIO_INTRADAY:
|
|
160
160
|
# Intraday primeiro; historical como fallback
|
|
161
161
|
df = intraday.intraday(codigos)
|
|
162
162
|
if not df.is_empty():
|
|
@@ -178,7 +178,7 @@ def available_dates(contract_code: str) -> pl.Series:
|
|
|
178
178
|
>>> from pyield.b3.futures import available_dates
|
|
179
179
|
>>> available_dates("DI1").head(3)
|
|
180
180
|
shape: (3,)
|
|
181
|
-
Series: '
|
|
181
|
+
Series: 'data_referencia' [date]
|
|
182
182
|
[
|
|
183
183
|
2018-01-02
|
|
184
184
|
2018-01-03
|
|
@@ -42,21 +42,21 @@ def adicionar_vencimento(
|
|
|
42
42
|
df: pl.DataFrame, codigo_contrato: str, coluna_ticker: str
|
|
43
43
|
) -> pl.DataFrame:
|
|
44
44
|
"""
|
|
45
|
-
Recebe um DataFrame Polars e ADICIONA a coluna '
|
|
45
|
+
Recebe um DataFrame Polars e ADICIONA a coluna 'data_vencimento'.
|
|
46
46
|
|
|
47
47
|
- Pega a coluna 'coluna_ticker'.
|
|
48
48
|
- Extrai o código de vencimento.
|
|
49
49
|
- Converte para a data "bruta", sem ajuste de feriado.
|
|
50
50
|
- Garante que a data de vencimento é um dia útil.
|
|
51
|
-
- Retorna o DataFrame com a nova coluna
|
|
51
|
+
- Retorna o DataFrame com a nova coluna data_vencimento.
|
|
52
52
|
|
|
53
|
-
Assume
|
|
53
|
+
Assume códigos de negociação no formato padrão de futuros da B3 (ex.: DI1F25).
|
|
54
54
|
"""
|
|
55
55
|
dia_vencimento = 15 if "DAP" in codigo_contrato else 1
|
|
56
56
|
df = df.with_columns(
|
|
57
57
|
pl.date(
|
|
58
58
|
# Ano: posição 4-5 (2 dígitos) -> Int -> Soma 2000
|
|
59
|
-
# Funciona para
|
|
59
|
+
# Funciona para códigos de negociação padrão de futuros (ex.: DI1F25)
|
|
60
60
|
year=pl.col(coluna_ticker).str.slice(4, 2).cast(pl.Int32, strict=False)
|
|
61
61
|
+ 2000,
|
|
62
62
|
# Mês: posição 3 (1 char = código do mês) -> mapeia para Int
|
|
@@ -64,9 +64,9 @@ def adicionar_vencimento(
|
|
|
64
64
|
.str.slice(3, 1)
|
|
65
65
|
.replace_strict(_MAPA_MESES, default=None, return_dtype=pl.Int8),
|
|
66
66
|
day=dia_vencimento,
|
|
67
|
-
).alias("
|
|
67
|
+
).alias("data_vencimento")
|
|
68
68
|
)
|
|
69
69
|
# Garante que a data de vencimento é um dia útil
|
|
70
|
-
df = df.with_columns(
|
|
70
|
+
df = df.with_columns(data_vencimento=bday.offset_expr("data_vencimento", 0))
|
|
71
71
|
|
|
72
72
|
return df
|