PYield 0.43.2__tar.gz → 0.43.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.43.2 → pyield-0.43.4}/PKG-INFO +9 -1
- {pyield-0.43.2 → pyield-0.43.4}/README.md +7 -0
- pyield-0.43.4/pyield/__about__.py +1 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/__init__.py +2 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/_internal/__init__.py +0 -1
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/difusao.py +11 -11
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/ettj_intraday.py +5 -5
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/ettj_last.py +5 -5
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/ima.py +22 -22
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/imaq.py +12 -12
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/common.py +20 -0
- pyield-0.43.4/pyield/b3/futures/core.py +117 -0
- pyield-0.43.4/pyield/b3/futures/historical/core.py +42 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/futures/historical/historical_b3.py +5 -8
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/futures/historical/historical_bmf.py +4 -4
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/futures/intraday.py +2 -3
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/price_report.py +18 -14
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/ptax_api.py +0 -4
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/repo.py +12 -12
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/trades_intraday.py +31 -31
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/trades_monthly.py +19 -19
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bday/core.py +20 -20
- {pyield-0.43.2 → pyield-0.43.4}/pyield/fwd.py +55 -35
- {pyield-0.43.2 → pyield-0.43.4}/pyield/interpolator.py +87 -82
- pyield-0.43.4/pyield/rmd.py +373 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/auctions.py +40 -40
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/benchmark.py +9 -9
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/lft.py +20 -20
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ltn.py +27 -27
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ntnb.py +32 -32
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ntnb1.py +2 -2
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ntnc.py +16 -16
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ntnf.py +28 -28
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/pre.py +6 -6
- {pyield-0.43.2 → pyield-0.43.4}/pyproject.toml +21 -4
- pyield-0.43.2/pyield/__about__.py +0 -1
- pyield-0.43.2/pyield/b3/futures/core.py +0 -141
- pyield-0.43.2/pyield/b3/futures/historical/core.py +0 -16
- {pyield-0.43.2 → pyield-0.43.4}/.gitignore +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/LICENSE +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/_internal/converters.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/_internal/data_cache.py +1 -1
- {pyield-0.43.2 → pyield-0.43.4}/pyield/_internal/retry.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/_internal/types.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/anbima/tpf.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/di1.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/di_over.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/futures/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/b3/futures/historical/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/auction.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/rates.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bc/vna.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bday/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bday/holidays/br_holidays_new.txt +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bday/holidays/br_holidays_old.txt +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/bday/holidays/brholidays.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/clock.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/ipca/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/ipca/historical.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/ipca/projected.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/py.typed +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/__init__.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/ntnbprinc.py +0 -0
- {pyield-0.43.2 → pyield-0.43.4}/pyield/tn/tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyield
|
|
3
|
-
Version: 0.43.
|
|
3
|
+
Version: 0.43.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
|
|
@@ -36,6 +36,7 @@ Classifier: Operating System :: OS Independent
|
|
|
36
36
|
Classifier: Programming Language :: Python :: 3
|
|
37
37
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
38
38
|
Requires-Python: >=3.12
|
|
39
|
+
Requires-Dist: fastexcel>=0.19.0
|
|
39
40
|
Requires-Dist: lxml>=6.0.0
|
|
40
41
|
Requires-Dist: polars>=1.33.0
|
|
41
42
|
Requires-Dist: python-dateutil>=2.8.0
|
|
@@ -148,6 +149,12 @@ Interpolator("flat_forward", known_bdays, known_rates, extrapolate=True)(100) #
|
|
|
148
149
|
|
|
149
150
|
Calcula taxas a termo a partir de curvas spot:
|
|
150
151
|
|
|
152
|
+
Convenção utilizada:
|
|
153
|
+
|
|
154
|
+
- `fwd_k = fwd_{j->k}` (forward do vértice `j` para `k`)
|
|
155
|
+
- `f_k = 1 + tx_k` (fator de capitalização no vértice `k`)
|
|
156
|
+
- `fwd_k = (f_k^au_k / f_j^au_j)^(1 / (au_k - au_j)) - 1`, com `au = du / 252`
|
|
157
|
+
|
|
151
158
|
```python
|
|
152
159
|
from pyield import forward, forwards
|
|
153
160
|
|
|
@@ -172,6 +179,7 @@ forwards(bdays, rates) # -> Series: [0.05, 0.070095, 0.090284]
|
|
|
172
179
|
| `anbima` | Dados da ANBIMA (preços de TPF, curvas de juros, índices IMA) |
|
|
173
180
|
| `bc` | Indicadores do BCB (SELIC, PTAX, repos, VNA) |
|
|
174
181
|
| `ipca` | Dados de inflação (histórico e projeções) |
|
|
182
|
+
| `rmd` | Relatório Mensal da Dívida do Tesouro Nacional |
|
|
175
183
|
| `today` / `now` | Data/hora atual no Brasil (America/Sao_Paulo) |
|
|
176
184
|
|
|
177
185
|
## Títulos Públicos
|
|
@@ -103,6 +103,12 @@ Interpolator("flat_forward", known_bdays, known_rates, extrapolate=True)(100) #
|
|
|
103
103
|
|
|
104
104
|
Calcula taxas a termo a partir de curvas spot:
|
|
105
105
|
|
|
106
|
+
Convenção utilizada:
|
|
107
|
+
|
|
108
|
+
- `fwd_k = fwd_{j->k}` (forward do vértice `j` para `k`)
|
|
109
|
+
- `f_k = 1 + tx_k` (fator de capitalização no vértice `k`)
|
|
110
|
+
- `fwd_k = (f_k^au_k / f_j^au_j)^(1 / (au_k - au_j)) - 1`, com `au = du / 252`
|
|
111
|
+
|
|
106
112
|
```python
|
|
107
113
|
from pyield import forward, forwards
|
|
108
114
|
|
|
@@ -127,6 +133,7 @@ forwards(bdays, rates) # -> Series: [0.05, 0.070095, 0.090284]
|
|
|
127
133
|
| `anbima` | Dados da ANBIMA (preços de TPF, curvas de juros, índices IMA) |
|
|
128
134
|
| `bc` | Indicadores do BCB (SELIC, PTAX, repos, VNA) |
|
|
129
135
|
| `ipca` | Dados de inflação (histórico e projeções) |
|
|
136
|
+
| `rmd` | Relatório Mensal da Dívida do Tesouro Nacional |
|
|
130
137
|
| `today` / `now` | Data/hora atual no Brasil (America/Sao_Paulo) |
|
|
131
138
|
|
|
132
139
|
## Títulos Públicos
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.43.4"
|
|
@@ -6,6 +6,7 @@ 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
|
|
9
|
+
from pyield.rmd import rmd
|
|
9
10
|
from pyield.tn import lft, ltn, ntnb, ntnb1, ntnbprinc, ntnc, ntnf, pre
|
|
10
11
|
|
|
11
12
|
__all__ = [
|
|
@@ -31,6 +32,7 @@ __all__ = [
|
|
|
31
32
|
"ntnc",
|
|
32
33
|
"ntnf",
|
|
33
34
|
"pre",
|
|
35
|
+
"rmd",
|
|
34
36
|
"bday",
|
|
35
37
|
]
|
|
36
38
|
|
|
@@ -181,17 +181,17 @@ def tpf_difusao(data_referencia: DateLike) -> pl.DataFrame:
|
|
|
181
181
|
não houver dados ou em caso de erro.
|
|
182
182
|
|
|
183
183
|
Output Columns:
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
184
|
+
- data_hora_referencia (datetime): Data e hora de referência da taxa.
|
|
185
|
+
- provedor (string): Provedor dos dados.
|
|
186
|
+
- titulo (string): Nome do título (ex: LFT, LTN).
|
|
187
|
+
- data_vencimento (date): Data de vencimento do título.
|
|
188
|
+
- codigo_isin (string): Código ISIN do título.
|
|
189
|
+
- dias_uteis (int): Dias úteis entre a data de referência e o vencimento.
|
|
190
|
+
- taxa_indicativa_anterior (float): Taxa indicativa de fechamento D-1 (decimal).
|
|
191
|
+
- taxa_venda (float): Taxa de oferta de venda (Ask rate) (decimal).
|
|
192
|
+
- taxa_compra (float): Taxa de oferta de compra (Bid rate) (decimal).
|
|
193
|
+
- taxa_media (float): Média entre a taxa de compra e venda (decimal).
|
|
194
|
+
- taxa_ultima (float): Última taxa negociada (decimal).
|
|
195
195
|
"""
|
|
196
196
|
if any_is_empty(data_referencia):
|
|
197
197
|
logger.warning("Nenhuma data fornecida. Retornando DataFrame vazio.")
|
|
@@ -109,11 +109,11 @@ def intraday_ettj() -> pl.DataFrame:
|
|
|
109
109
|
pl.DataFrame: DataFrame com os dados intradiários da ETTJ.
|
|
110
110
|
|
|
111
111
|
Output Columns:
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
- date (Date): data de referência da curva de juros.
|
|
113
|
+
- vertex (Int64): vértice em dias úteis.
|
|
114
|
+
- nominal_rate (Float64): taxa de juros nominal zero-cupom.
|
|
115
|
+
- real_rate (Float64): taxa de juros real zero-cupom (indexada ao IPCA).
|
|
116
|
+
- implied_inflation (Float64): taxa de inflação implícita (breakeven).
|
|
117
117
|
|
|
118
118
|
Note:
|
|
119
119
|
Todas as taxas são expressas em formato decimal (ex: 0.12 para 12%).
|
|
@@ -98,11 +98,11 @@ def last_ettj() -> pl.DataFrame:
|
|
|
98
98
|
pl.DataFrame: DataFrame com os dados da ETTJ de fechamento.
|
|
99
99
|
|
|
100
100
|
Output Columns:
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
101
|
+
- date (Date): data de referência da curva de juros.
|
|
102
|
+
- vertex (Int64): vértice em dias úteis.
|
|
103
|
+
- nominal_rate (Float64): taxa de juros nominal zero-cupom.
|
|
104
|
+
- real_rate (Float64): taxa de juros real zero-cupom (indexada ao IPCA).
|
|
105
|
+
- implied_inflation (Float64): taxa de inflação implícita (breakeven).
|
|
106
106
|
|
|
107
107
|
Note:
|
|
108
108
|
Todas as taxas são expressas em formato decimal (ex: 0.12 para 12%).
|
|
@@ -141,28 +141,28 @@ def last_ima(ima_type: TiposIMA | None = None) -> pl.DataFrame:
|
|
|
141
141
|
em caso de erro.
|
|
142
142
|
|
|
143
143
|
Output Columns:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
144
|
+
- Date (Date): data de referência.
|
|
145
|
+
- IMAType (String): tipo de índice IMA (ex: 'IMA-B', 'IRF-M').
|
|
146
|
+
- BondType (String): tipo de título (ex: 'LTN', 'NTN-B').
|
|
147
|
+
- Maturity (Date): data de vencimento do título.
|
|
148
|
+
- SelicCode (Int64): código do título no sistema SELIC.
|
|
149
|
+
- ISIN (String): código ISIN (International Securities Identification Number).
|
|
150
|
+
- BDToMat (Int64): dias úteis até o vencimento.
|
|
151
|
+
- Duration (Float64): duration do título em anos úteis (252 d.u./ano).
|
|
152
|
+
- IndicativeRate (Float64): taxa indicativa em decimal (ex: 0.10 para 10%).
|
|
153
|
+
- Price (Float64): preço unitário (PU) em R$.
|
|
154
|
+
- InterestPrice (Float64): PU de juros em R$.
|
|
155
|
+
- DV01 (Float64): DV01 em R$.
|
|
156
|
+
- PMR (Float64): prazo médio de repactuação.
|
|
157
|
+
- Weight (Float64): peso do título no índice (%).
|
|
158
|
+
- Convexity (Float64): convexidade do título.
|
|
159
|
+
- TheoreticalQuantity (Float64): quantidade teórica (em 1.000 títulos).
|
|
160
|
+
- NumberOfOperations (Int64): número de operações.
|
|
161
|
+
- NegotiatedQuantity (Int64): quantidade negociada (unidades).
|
|
162
|
+
- NegotiatedValue (Float64): valor negociado em R$.
|
|
163
|
+
- MarketDV01 (Float64): DV01 de mercado em R$.
|
|
164
|
+
- MarketQuantity (Int64): quantidade em carteira (unidades).
|
|
165
|
+
- MarketValue (Float64): valor de mercado em R$.
|
|
166
166
|
|
|
167
167
|
Examples:
|
|
168
168
|
>>> from pyield import anbima
|
|
@@ -221,18 +221,18 @@ def imaq(date: DateLike) -> pl.DataFrame:
|
|
|
221
221
|
vazio e registra log da exceção.
|
|
222
222
|
|
|
223
223
|
Output Columns:
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
224
|
+
- Date (Date): data de referência dos dados.
|
|
225
|
+
- BondType (String): tipo do título (LTN, NTN-B, NTN-F, LFT, …).
|
|
226
|
+
- MaturityDate (Date): data de vencimento do título.
|
|
227
|
+
- SelicCode (Int64): código SELIC do título.
|
|
228
|
+
- ISIN (String): código ISIN (International Securities Id Number).
|
|
229
|
+
- Price (Float64): PU do título em R$.
|
|
230
|
+
- MarketQuantity (Int64): quantidade em mercado (unidades).
|
|
231
|
+
- MarketDV01 (Int64): DV01 do estoque em R$.
|
|
232
|
+
- MarketDV01USD (Int64): DV01 do estoque em USD.
|
|
233
|
+
- MarketValue (Int64): valor de mercado em R$.
|
|
234
|
+
- QuantityVariation (Int64): variação diária da quantidade.
|
|
235
|
+
- BondStatus (String): status do título.
|
|
236
236
|
|
|
237
237
|
Notes:
|
|
238
238
|
- Valores convertidos para unidades puras (ex: MarketQuantity × 1.000).
|
|
@@ -8,6 +8,26 @@ from pyield import bday, clock
|
|
|
8
8
|
registro = logging.getLogger(__name__)
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
def expr_dv01(
|
|
12
|
+
coluna_dias_uteis: str,
|
|
13
|
+
coluna_taxa: str,
|
|
14
|
+
coluna_preco: str,
|
|
15
|
+
) -> pl.Expr:
|
|
16
|
+
"""Retorna a expressão Polars para cálculo de DV01.
|
|
17
|
+
|
|
18
|
+
Fórmula:
|
|
19
|
+
DV01 = (Duration / (1 + taxa)) * preço * 0,0001
|
|
20
|
+
|
|
21
|
+
Onde:
|
|
22
|
+
- Duration = dias_uteis / 252
|
|
23
|
+
- taxa deve estar em formato decimal (ex.: 0.145)
|
|
24
|
+
- preço é o PU do contrato.
|
|
25
|
+
"""
|
|
26
|
+
duracao = pl.col(coluna_dias_uteis) / 252
|
|
27
|
+
duracao_modificada = duracao / (1 + pl.col(coluna_taxa))
|
|
28
|
+
return 0.0001 * duracao_modificada * pl.col(coluna_preco)
|
|
29
|
+
|
|
30
|
+
|
|
11
31
|
def adicionar_vencimento(
|
|
12
32
|
df: pl.DataFrame, codigo_contrato: str, coluna_ticker: str
|
|
13
33
|
) -> pl.DataFrame:
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
import polars as pl
|
|
6
|
+
|
|
7
|
+
import pyield._internal.converters as cv
|
|
8
|
+
import pyield.b3.common as cm
|
|
9
|
+
import pyield.b3.futures.historical.core as hcore
|
|
10
|
+
from pyield import clock
|
|
11
|
+
from pyield._internal.types import DateLike, any_is_empty
|
|
12
|
+
from pyield.b3.futures.intraday import fetch_intraday_df
|
|
13
|
+
|
|
14
|
+
OpcoesContrato = Literal["DI1", "DDI", "FRC", "DAP", "DOL", "WDO", "IND", "WIN"]
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# Pregão abre às 9:00, porém os dados têm atraso de 15 minutos.
|
|
19
|
+
# Esperar 1 minuto adicional para garantir que estejam disponíveis (9:16h).
|
|
20
|
+
HORA_INICIO_INTRADAY = dt.time(9, 16)
|
|
21
|
+
# Pregão fecha às 18:00h, momento em que os dados consolidados começam a ser preparados.
|
|
22
|
+
HORA_FIM_INTRADAY = dt.time(18, 30)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _data_intraday_valida(data_verificacao: dt.date) -> bool:
|
|
26
|
+
"""Verifica se a data é um dia de negociação intraday."""
|
|
27
|
+
# Primeiro valida regra geral de dia futuro / não útil / datas especiais
|
|
28
|
+
if not cm.data_negociacao_valida(data_verificacao):
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
# Intraday só existe para 'hoje'
|
|
32
|
+
hoje_brasil = clock.today()
|
|
33
|
+
if data_verificacao != hoje_brasil:
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
return True
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def futures(
|
|
40
|
+
date: DateLike,
|
|
41
|
+
contract_code: OpcoesContrato | str,
|
|
42
|
+
) -> pl.DataFrame:
|
|
43
|
+
"""
|
|
44
|
+
Fetches data for a specified futures contract based on type and reference date.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
contract_code (str): The B3 futures contract code identifying the derivative.
|
|
48
|
+
Supported contract codes are:
|
|
49
|
+
- "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
|
|
50
|
+
- "DDI": DI x U.S. Dollar Spread Futures (Futuro de Cupom Cambial) from B3.
|
|
51
|
+
- "FRC": Forward Rate Agreement (FRA).
|
|
52
|
+
- "FRO": FRA DE CUPOM CAMBIAL EM OC1
|
|
53
|
+
- "DAP": DI x IPCA Spread Futures.
|
|
54
|
+
- "DOL": U.S. Dollar Futures from B3.
|
|
55
|
+
- "WDO": Mini U.S. Dollar Futures from B3.
|
|
56
|
+
- "IND": Ibovespa Futures from B3.
|
|
57
|
+
- "WIN": Mini Ibovespa Futures from B3.
|
|
58
|
+
date (DateLike): The reference date for fetching the data.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
pl.DataFrame: DataFrame containing the fetched data for the specified futures
|
|
62
|
+
contract.
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
ValueError: If the futures contract code is not recognized or supported.
|
|
66
|
+
|
|
67
|
+
Examples:
|
|
68
|
+
>>> df = futures("31-05-2024", "DI1")
|
|
69
|
+
>>> {"TradeDate", "TickerSymbol", "ExpirationDate", "SettlementRate"}.issubset(
|
|
70
|
+
... set(df.columns)
|
|
71
|
+
... )
|
|
72
|
+
True
|
|
73
|
+
>>> df.shape[0] > 0
|
|
74
|
+
True
|
|
75
|
+
|
|
76
|
+
>>> df = futures("31-05-2024", "DAP")
|
|
77
|
+
>>> {"TradeDate", "TickerSymbol", "ExpirationDate", "SettlementRate"}.issubset(
|
|
78
|
+
... set(df.columns)
|
|
79
|
+
... )
|
|
80
|
+
True
|
|
81
|
+
>>> df.shape[0] > 0
|
|
82
|
+
True
|
|
83
|
+
|
|
84
|
+
""" # noqa: E501
|
|
85
|
+
if any_is_empty(date, contract_code):
|
|
86
|
+
return pl.DataFrame()
|
|
87
|
+
data_negociacao = cv.converter_datas(date)
|
|
88
|
+
|
|
89
|
+
# Validação centralizada (evita chamadas desnecessárias às APIs B3)
|
|
90
|
+
if not cm.data_negociacao_valida(data_negociacao):
|
|
91
|
+
logger.warning(
|
|
92
|
+
"A data %s não é válida. Retornando DataFrame vazio.",
|
|
93
|
+
data_negociacao,
|
|
94
|
+
)
|
|
95
|
+
return pl.DataFrame()
|
|
96
|
+
|
|
97
|
+
contrato_selecionado = str(contract_code).upper()
|
|
98
|
+
|
|
99
|
+
if _data_intraday_valida(data_negociacao):
|
|
100
|
+
# É um dia de negociação intraday
|
|
101
|
+
horario_atual = clock.now().time()
|
|
102
|
+
if horario_atual < HORA_INICIO_INTRADAY: # Mercado não está aberto ainda
|
|
103
|
+
logger.warning("Mercado ainda não abriu. Retornando DataFrame vazio.")
|
|
104
|
+
return pl.DataFrame()
|
|
105
|
+
|
|
106
|
+
# Existe a chance de que os dados consolidados estejam disponíveis após as 18h
|
|
107
|
+
if horario_atual >= HORA_FIM_INTRADAY:
|
|
108
|
+
df_hist = hcore.buscar_df_historico(data_negociacao, contrato_selecionado)
|
|
109
|
+
if not df_hist.is_empty():
|
|
110
|
+
logger.info("Dados consolidados disponíveis. Usando histórico.")
|
|
111
|
+
return df_hist
|
|
112
|
+
|
|
113
|
+
# Mercado está aberto e não há dados consolidados disponíveis ainda
|
|
114
|
+
return fetch_intraday_df(contrato_selecionado)
|
|
115
|
+
|
|
116
|
+
else: # É um dia histórico
|
|
117
|
+
return hcore.buscar_df_historico(data_negociacao, contrato_selecionado)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
|
|
3
|
+
import polars as pl
|
|
4
|
+
from dateutil.relativedelta import relativedelta
|
|
5
|
+
|
|
6
|
+
from pyield import clock
|
|
7
|
+
from pyield.b3.price_report import fetch_price_report
|
|
8
|
+
|
|
9
|
+
from . import historical_b3 as hb3
|
|
10
|
+
|
|
11
|
+
JANELA_DADOS_RECENTES = relativedelta(months=1)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def buscar_df_historico(data: dt.date, codigo_contrato: str) -> pl.DataFrame:
|
|
15
|
+
"""Busca o histórico com priorização de fonte por recência da data.
|
|
16
|
+
|
|
17
|
+
Regras:
|
|
18
|
+
- Data até 1 mês atrás: busca direto no Price Report (SPR).
|
|
19
|
+
- Data dentro da janela recente: tenta endpoint curto (`historical_b3`).
|
|
20
|
+
- Se o endpoint curto vier vazio: faz fallback para Price Report (SPR).
|
|
21
|
+
"""
|
|
22
|
+
data_limite_recente = clock.today() - JANELA_DADOS_RECENTES
|
|
23
|
+
|
|
24
|
+
# Datas antigas não tentam endpoint de janela curta.
|
|
25
|
+
if data <= data_limite_recente:
|
|
26
|
+
try:
|
|
27
|
+
return fetch_price_report(
|
|
28
|
+
date=data, contract_code=codigo_contrato, source_type="SPR"
|
|
29
|
+
)
|
|
30
|
+
except Exception:
|
|
31
|
+
return pl.DataFrame()
|
|
32
|
+
|
|
33
|
+
# Datas recentes priorizam endpoint curto; fallback para price report.
|
|
34
|
+
df_recente = hb3.buscar_df_historico(data, codigo_contrato)
|
|
35
|
+
if not df_recente.is_empty():
|
|
36
|
+
return df_recente
|
|
37
|
+
try:
|
|
38
|
+
return fetch_price_report(
|
|
39
|
+
date=data, contract_code=codigo_contrato, source_type="SPR"
|
|
40
|
+
)
|
|
41
|
+
except Exception:
|
|
42
|
+
return pl.DataFrame()
|
|
@@ -67,7 +67,6 @@ def _buscar_csv(data: dt.date) -> bytes:
|
|
|
67
67
|
url, params=parametros, json=carga, headers=cabecalhos, timeout=(5, 30)
|
|
68
68
|
)
|
|
69
69
|
resposta.raise_for_status()
|
|
70
|
-
resposta.encoding = "utf-8-sig"
|
|
71
70
|
return resposta.content
|
|
72
71
|
|
|
73
72
|
|
|
@@ -121,7 +120,7 @@ def _processar_df(
|
|
|
121
120
|
colunas_taxa = [c for c in df.columns if c.endswith("Rate")]
|
|
122
121
|
|
|
123
122
|
# Divide por 100 para transformar percentual em decimal (14.50 -> 0.1450)
|
|
124
|
-
df = df.with_columns(
|
|
123
|
+
df = df.with_columns(pl.col(colunas_taxa).truediv(100).round(6))
|
|
125
124
|
|
|
126
125
|
# 4. Cálculo do DV01 (Apenas para DI1 e se tivermos as colunas necessárias)
|
|
127
126
|
# SettlementPrice aqui já é o PU vindo do CSV (Ex: 99.000)
|
|
@@ -131,11 +130,9 @@ def _processar_df(
|
|
|
131
130
|
and "SettlementPrice" in df.columns
|
|
132
131
|
and "SettlementRate" in df.columns
|
|
133
132
|
):
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
duracao_mod = duracao / (1 + pl.col("SettlementRate"))
|
|
138
|
-
df = df.with_columns(DV01=duracao_mod * pl.col("SettlementPrice") * 0.0001)
|
|
133
|
+
df = df.with_columns(
|
|
134
|
+
DV01=cm.expr_dv01("BDaysToExp", "SettlementRate", "SettlementPrice")
|
|
135
|
+
)
|
|
139
136
|
|
|
140
137
|
# 5. Forward Rates (Para DI1 e DAP)
|
|
141
138
|
if codigo_contrato in {"DI1", "DAP"} and "SettlementRate" in df.columns:
|
|
@@ -191,7 +188,7 @@ def _selecionar_e_reordenar_colunas(df: pl.DataFrame) -> pl.DataFrame:
|
|
|
191
188
|
return df.select(colunas_existentes)
|
|
192
189
|
|
|
193
190
|
|
|
194
|
-
def
|
|
191
|
+
def buscar_df_historico(data: dt.date, codigo_contrato: str) -> pl.DataFrame:
|
|
195
192
|
"""Busca o histórico de futuros na B3 para a data informada."""
|
|
196
193
|
try:
|
|
197
194
|
# Tenta baixar os dados
|
|
@@ -279,9 +279,9 @@ def _adicionar_colunas_derivadas(
|
|
|
279
279
|
if codigo_contrato == "DI1" and {"SettlementRate", "SettlementPrice"}.issubset(
|
|
280
280
|
df.columns
|
|
281
281
|
):
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
282
|
+
df = df.with_columns(
|
|
283
|
+
DV01=cm.expr_dv01("BDaysToExp", "SettlementRate", "SettlementPrice")
|
|
284
|
+
)
|
|
285
285
|
|
|
286
286
|
if codigo_contrato in {"DI1", "DAP"} and "SettlementRate" in df.columns:
|
|
287
287
|
df = df.with_columns(
|
|
@@ -290,7 +290,7 @@ def _adicionar_colunas_derivadas(
|
|
|
290
290
|
return df
|
|
291
291
|
|
|
292
292
|
|
|
293
|
-
def
|
|
293
|
+
def buscar_df_historico(data: dt.date, codigo_contrato: str) -> pl.DataFrame:
|
|
294
294
|
"""Obtém e processa o histórico da BMF para um contrato e data."""
|
|
295
295
|
html_texto = _buscar_html(data, codigo_contrato)
|
|
296
296
|
df = _parsear_html_lxml(html_texto)
|
|
@@ -34,6 +34,7 @@ import polars as pl
|
|
|
34
34
|
import polars.selectors as cs
|
|
35
35
|
import requests
|
|
36
36
|
|
|
37
|
+
import pyield.b3.common as cm
|
|
37
38
|
from pyield import bday, clock
|
|
38
39
|
from pyield._internal.retry import retry_padrao
|
|
39
40
|
from pyield.fwd import forwards
|
|
@@ -142,9 +143,7 @@ def _processar_df(df: pl.DataFrame, codigo_contrato: str) -> pl.DataFrame:
|
|
|
142
143
|
)
|
|
143
144
|
|
|
144
145
|
if codigo_contrato == "DI1": # Adiciona DV01 para DI1
|
|
145
|
-
|
|
146
|
-
duracao_mod = duracao / (1 + pl.col("LastRate"))
|
|
147
|
-
df = df.with_columns(DV01=0.0001 * duracao_mod * pl.col("LastPrice"))
|
|
146
|
+
df = df.with_columns(DV01=cm.expr_dv01("BDaysToExp", "LastRate", "LastPrice"))
|
|
148
147
|
|
|
149
148
|
return df.filter(pl.col("DaysToExp") > 0) # Remove expiring contracts
|
|
150
149
|
|
|
@@ -275,9 +275,9 @@ def _processar_df(df: pl.DataFrame, contract_code: str) -> pl.DataFrame:
|
|
|
275
275
|
if contract_code == "DI1":
|
|
276
276
|
# DV01 requer SettlementRate e SettlementPrice
|
|
277
277
|
if "SettlementRate" in df.columns and "SettlementPrice" in df.columns:
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
278
|
+
df = df.with_columns(
|
|
279
|
+
DV01=cm.expr_dv01("BDaysToExp", "SettlementRate", "SettlementPrice")
|
|
280
|
+
)
|
|
281
281
|
|
|
282
282
|
if contract_code in {"DI1", "DAP"} and "SettlementRate" in df.columns:
|
|
283
283
|
forward_rates = forwards(bdays=df["BDaysToExp"], rates=df["SettlementRate"])
|
|
@@ -287,9 +287,7 @@ def _processar_df(df: pl.DataFrame, contract_code: str) -> pl.DataFrame:
|
|
|
287
287
|
return df.select(coluna_ordem).filter(pl.col("DaysToExp") > 0)
|
|
288
288
|
|
|
289
289
|
|
|
290
|
-
def _processar_zip(
|
|
291
|
-
zip_data: bytes, contract_code: str, source_type: str = "SPR"
|
|
292
|
-
) -> pl.DataFrame:
|
|
290
|
+
def _processar_zip(zip_data: bytes, contract_code: str) -> pl.DataFrame:
|
|
293
291
|
if not zip_data:
|
|
294
292
|
registro.warning("ZIP XML vazio.")
|
|
295
293
|
return pl.DataFrame()
|
|
@@ -341,19 +339,25 @@ def fetch_price_report(
|
|
|
341
339
|
Returns:
|
|
342
340
|
pl.DataFrame: DataFrame com colunas ordenadas conforme COLUNAS_SAIDA,
|
|
343
341
|
filtrado para excluir contratos vencidos (DaysToExp <= 0). Retorna
|
|
344
|
-
DataFrame vazio
|
|
342
|
+
DataFrame vazio para data inválida, resposta vazia ou falhas de parsing
|
|
343
|
+
recuperáveis.
|
|
345
344
|
|
|
346
345
|
Raises:
|
|
347
346
|
ValueError: Se source_type for inválido.
|
|
348
|
-
DadoIndisponivelError: Se a data for válida mas não
|
|
349
|
-
|
|
347
|
+
DadoIndisponivelError: Se a data for válida, mas o endpoint não fornecer
|
|
348
|
+
arquivo para a data consultada.
|
|
349
|
+
requests.HTTPError: Se a requisição HTTP ao endpoint falhar.
|
|
350
350
|
|
|
351
351
|
Examples:
|
|
352
352
|
>>> import pyield as yd
|
|
353
353
|
>>> df = yd.b3.fetch_price_report("26-04-2024", "DI1")
|
|
354
|
-
>>> df.columns[:5]
|
|
355
|
-
|
|
356
|
-
|
|
354
|
+
>>> df.is_empty() or df.columns[:5] == [
|
|
355
|
+
... "TradeDate",
|
|
356
|
+
... "TickerSymbol",
|
|
357
|
+
... "ExpirationDate",
|
|
358
|
+
... "BDaysToExp",
|
|
359
|
+
... "DaysToExp",
|
|
360
|
+
... ]
|
|
357
361
|
True
|
|
358
362
|
|
|
359
363
|
>>> # Feriado ou fim de semana (retorna DataFrame vazio)
|
|
@@ -379,7 +383,7 @@ def fetch_price_report(
|
|
|
379
383
|
registro.warning(msg_vazia)
|
|
380
384
|
return pl.DataFrame()
|
|
381
385
|
|
|
382
|
-
df = _processar_zip(dados_zip, contract_code
|
|
386
|
+
df = _processar_zip(dados_zip, contract_code)
|
|
383
387
|
|
|
384
388
|
if df.is_empty():
|
|
385
389
|
registro.warning(msg_vazia)
|
|
@@ -420,5 +424,5 @@ def read_price_report(
|
|
|
420
424
|
source_type = "SPR" if filename.startswith("SPRD") else "PR"
|
|
421
425
|
|
|
422
426
|
dados_zip = _ler_zip_arquivo(file_path)
|
|
423
|
-
df = _processar_zip(dados_zip, contract_code
|
|
427
|
+
df = _processar_zip(dados_zip, contract_code)
|
|
424
428
|
return df
|
|
@@ -110,7 +110,6 @@ def ptax_series(
|
|
|
110
110
|
https://olinda.bcb.gov.br/olinda/servico/PTAX/versao/v1/odata/CotacaoDolarPeriodo(dataInicial=@dataInicial,dataFinalCotacao=@dataFinalCotacao)?@dataInicial='08-01-2025'&@dataFinalCotacao='08-05-2025'&$format=text/csv
|
|
111
111
|
|
|
112
112
|
Consultas de Período:
|
|
113
|
-
|
|
114
113
|
- Para consultar dados de um intervalo, forneça as datas de `start` e `end`.
|
|
115
114
|
Exemplo:
|
|
116
115
|
|
|
@@ -127,7 +126,6 @@ def ptax_series(
|
|
|
127
126
|
`ptax_series(end='27-10-2024')`
|
|
128
127
|
|
|
129
128
|
Série Histórica Completa:
|
|
130
|
-
|
|
131
129
|
- Para recuperar a série histórica completa de leilões (desde 28.11.1984
|
|
132
130
|
até o último dia útil), chame a função sem fornecer os parâmetros `start` e `end`.
|
|
133
131
|
Exemplo:
|
|
@@ -135,7 +133,6 @@ def ptax_series(
|
|
|
135
133
|
`ptax_series()`
|
|
136
134
|
|
|
137
135
|
Busca dados de cotações de dólar PTAX (taxa de câmbio) para o período:
|
|
138
|
-
|
|
139
136
|
- Se `start` for fornecido e `end` não, a função retorna dados de `start` até o fim.
|
|
140
137
|
- Se `end` for fornecido e `start` não, a API retorna dados do início até `end`.
|
|
141
138
|
- Se ambos `start` e `end` forem omitidos, a API retorna a série histórica completa.
|
|
@@ -189,7 +186,6 @@ def ptax_series(
|
|
|
189
186
|
- Última data disponível: data atual
|
|
190
187
|
|
|
191
188
|
O DataFrame possui as seguintes colunas:
|
|
192
|
-
|
|
193
189
|
- Date: Data da cotação.
|
|
194
190
|
- DateTime: Data e hora da cotação.
|
|
195
191
|
- BuyRate: Taxa de compra.
|
|
@@ -174,18 +174,18 @@ def repos(
|
|
|
174
174
|
retorna DataFrame vazio e registra log da exceção.
|
|
175
175
|
|
|
176
176
|
Output Columns:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
177
|
+
- data_leilao (Date): data de ocorrência do leilão.
|
|
178
|
+
- data_liquidacao (Date): data de liquidação (início da operação).
|
|
179
|
+
- data_retorno (Date): data de recompra / término da operação.
|
|
180
|
+
- hora_inicio (Time): horário de início do leilão.
|
|
181
|
+
- prazo_dias_corridos (Int64): dias corridos até a data de retorno.
|
|
182
|
+
- prazo_dias_uteis (Int64): dias úteis entre liquidação e retorno (bday.count).
|
|
183
|
+
- numero_comunicado (Int64): número do comunicado/aviso do BC (pode ser nulo).
|
|
184
|
+
- tipo_oferta (String): classif. do tipo de oferta (ex: Tomador, Compromissada 1047).
|
|
185
|
+
- publico_permitido (String): escopo de participantes (SomenteDealer, TodoMercado).
|
|
186
|
+
- volume_aceito (Int64): volume aceito no leilão em reais (convertido de milhares).
|
|
187
|
+
- taxa_corte (Float64): taxa de corte (ex. 0.1490 = 14,90%). Nula se volume_aceito = 0.
|
|
188
|
+
- percentual_aceito (Float64): percentual do volume ofertado efetivamente aceito (0-100).
|
|
189
189
|
100 = nenhuma rejeição. 0 indica nada aceito (volume_aceito = 0).
|
|
190
190
|
|
|
191
191
|
Notes:
|