PYield 0.37.2__tar.gz → 0.37.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.37.2 → pyield-0.37.4}/PKG-INFO +1 -1
- pyield-0.37.4/pyield/__about__.py +1 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/__init__.py +2 -2
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/tpf.py +97 -94
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/di1.py +40 -13
- pyield-0.37.2/pyield/__about__.py +0 -1
- {pyield-0.37.2 → pyield-0.37.4}/.gitignore +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/LICENSE +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/README.md +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/ettj.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/ima.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/imaq.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/anbima/ipca.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/di_over.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/futures/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/futures/common.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/futures/historical.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/futures/intraday.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/b3/futures/xml.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/auction.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/ptax_api.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/rates.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/repo.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/trades_intraday.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/trades_monthly.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bc/vna.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/bday.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/conftest.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/data_cache.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/date_converter.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/fwd.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/holidays/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/ibge/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/ibge/ipca.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/interpolator.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/py.typed +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/retry.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/__init__.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/benchmark.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/lft.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/ltn.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/ntnb.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/ntnc.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/ntnf.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/pre.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyield/tn/tools.py +0 -0
- {pyield-0.37.2 → pyield-0.37.4}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.37.4"
|
|
@@ -2,13 +2,13 @@ from pyield.anbima.ettj import intraday_ettj, last_ettj
|
|
|
2
2
|
from pyield.anbima.ima import last_ima
|
|
3
3
|
from pyield.anbima.imaq import imaq
|
|
4
4
|
from pyield.anbima.ipca import ipca_projection
|
|
5
|
-
from pyield.anbima.tpf import
|
|
5
|
+
from pyield.anbima.tpf import _fetch_tpf_data, tpf_data, tpf_fixed_rate_maturities
|
|
6
6
|
|
|
7
7
|
__all__ = [
|
|
8
8
|
"last_ima",
|
|
9
9
|
"imaq",
|
|
10
10
|
"ipca_projection",
|
|
11
|
-
"
|
|
11
|
+
"_fetch_tpf_data",
|
|
12
12
|
"tpf_data",
|
|
13
13
|
"tpf_fixed_rate_maturities",
|
|
14
14
|
"last_ettj",
|
|
@@ -8,6 +8,7 @@ from zoneinfo import ZoneInfo
|
|
|
8
8
|
import pandas as pd
|
|
9
9
|
|
|
10
10
|
from pyield import bday
|
|
11
|
+
from pyield.b3 import di1
|
|
11
12
|
from pyield.bc.ptax_api import ptax
|
|
12
13
|
from pyield.data_cache import get_cached_dataset
|
|
13
14
|
from pyield.date_converter import DateScalar, convert_input_dates
|
|
@@ -106,7 +107,7 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
|
|
|
106
107
|
return df
|
|
107
108
|
|
|
108
109
|
|
|
109
|
-
def
|
|
110
|
+
def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
110
111
|
df["BDToMat"] = bday.count(start=df["ReferenceDate"], end=df["MaturityDate"])
|
|
111
112
|
|
|
112
113
|
df["Duration"] = 0.0
|
|
@@ -167,6 +168,18 @@ def _add_usd_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
167
168
|
return df
|
|
168
169
|
|
|
169
170
|
|
|
171
|
+
def _add_di_rate(df: pd.DataFrame) -> pd.DataFrame:
|
|
172
|
+
"""Add the DI rate column to the DataFrame."""
|
|
173
|
+
# INSERIR OS DADOS DO DI INTERPOLADO ###
|
|
174
|
+
reference_date = df["ReferenceDate"].iloc[0]
|
|
175
|
+
df["DIRate"] = di1.interpolate_rates(
|
|
176
|
+
dates=reference_date,
|
|
177
|
+
expirations=df["MaturityDate"],
|
|
178
|
+
extrapolate=True,
|
|
179
|
+
)
|
|
180
|
+
return df
|
|
181
|
+
|
|
182
|
+
|
|
170
183
|
def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
171
184
|
"""Reorder the columns of the DataFrame according to the specified order."""
|
|
172
185
|
column_order = [
|
|
@@ -183,6 +196,7 @@ def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
183
196
|
"BidRate",
|
|
184
197
|
"AskRate",
|
|
185
198
|
"IndicativeRate",
|
|
199
|
+
"DIRate",
|
|
186
200
|
"StdDev",
|
|
187
201
|
"LowerBoundRateD0",
|
|
188
202
|
"UpperBoundRateD0",
|
|
@@ -194,65 +208,22 @@ def _reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
194
208
|
return df[column_order].copy()
|
|
195
209
|
|
|
196
210
|
|
|
197
|
-
def
|
|
198
|
-
"""
|
|
199
|
-
main_columns = [
|
|
200
|
-
"BondType",
|
|
201
|
-
"ReferenceDate",
|
|
202
|
-
"MaturityDate",
|
|
203
|
-
"BDToMat",
|
|
204
|
-
"Duration",
|
|
205
|
-
"DV01",
|
|
206
|
-
"DV01USD",
|
|
207
|
-
"Price",
|
|
208
|
-
"BidRate",
|
|
209
|
-
"AskRate",
|
|
210
|
-
"IndicativeRate",
|
|
211
|
-
]
|
|
212
|
-
main_columns = [col for col in main_columns if col in df.columns]
|
|
213
|
-
return df[main_columns].copy()
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
def fetch_tpf_data(
|
|
217
|
-
date: DateScalar,
|
|
218
|
-
bond_type: str | BOND_TYPES | None = None,
|
|
219
|
-
all_columns: bool = True,
|
|
220
|
-
) -> pd.DataFrame:
|
|
221
|
-
"""Fetch and process TPF secondary market data directly from the ANBIMA website.
|
|
222
|
-
This is a low-level function intended for internal use or specific backend
|
|
223
|
-
jobs. For general use, prefer `tpf_data` which includes caching logic.
|
|
224
|
-
|
|
225
|
-
Only the last 5 days of data are available in the ANBIMA website. Data older
|
|
226
|
-
than 5 business days is only available through the RTM network.
|
|
211
|
+
def _fetch_tpf_data(date: pd.Timestamp) -> pd.DataFrame:
|
|
212
|
+
"""Fetch and process TPF secondary market data directly from the ANBIMA source.
|
|
227
213
|
|
|
228
|
-
This
|
|
229
|
-
|
|
230
|
-
and
|
|
214
|
+
This is a low-level function intended for internal use. It handles the logic
|
|
215
|
+
of building the correct URL (public or RTM), downloading the data with retries,
|
|
216
|
+
and processing it into a structured DataFrame.
|
|
231
217
|
|
|
232
218
|
Args:
|
|
233
|
-
date (
|
|
234
|
-
bond_type (str, optional): Filter data by bond type.
|
|
235
|
-
Defaults to None, which returns data for all bond types.
|
|
236
|
-
all_columns (bool, optional): If True, all columns are returned.
|
|
237
|
-
Defaults to True. If False, only the main columns are returned.
|
|
219
|
+
date (pd.Timestamp): The reference date for the data.
|
|
238
220
|
|
|
239
221
|
Returns:
|
|
240
|
-
pd.DataFrame: A DataFrame containing bond market data
|
|
241
|
-
|
|
242
|
-
specified date (weekends, holidays, unavailable data, or lack of
|
|
243
|
-
RTM access for older data).
|
|
222
|
+
pd.DataFrame: A DataFrame containing processed bond market data, or an
|
|
223
|
+
empty DataFrame if data is unavailable or a connection error occurs.
|
|
244
224
|
"""
|
|
245
|
-
date = convert_input_dates(date)
|
|
246
|
-
date_log = date.strftime("%d/%m/%Y")
|
|
247
|
-
today = dt.datetime.now(BZ_TIMEZONE).date()
|
|
248
|
-
if date.date() > today:
|
|
249
|
-
logger.info(
|
|
250
|
-
f"Cannot fetch data for a future date ({date_log}). "
|
|
251
|
-
"Returning empty DataFrame."
|
|
252
|
-
)
|
|
253
|
-
return pd.DataFrame()
|
|
254
|
-
|
|
255
225
|
file_url = _build_file_url(date)
|
|
226
|
+
date_str = date.strftime("%d/%m/%Y")
|
|
256
227
|
|
|
257
228
|
# --- "FAIL-FAST" PARA EVITAR RETRIES DESNECESSÁRIOS NA RTM ---
|
|
258
229
|
if ANBIMA_RTM_URL in file_url:
|
|
@@ -263,7 +234,7 @@ def fetch_tpf_data(
|
|
|
263
234
|
# Se falhar (gaierror = get address info error), não estamos na RTM.
|
|
264
235
|
# Não adianta prosseguir para a função com retry.
|
|
265
236
|
logger.warning(
|
|
266
|
-
f"Could not resolve RTM host for {
|
|
237
|
+
f"Could not resolve RTM host for {date_str}. This is expected if "
|
|
267
238
|
"you are not on the RTM network. Historical data requires RTM access. "
|
|
268
239
|
"Returning empty DataFrame."
|
|
269
240
|
)
|
|
@@ -274,40 +245,35 @@ def fetch_tpf_data(
|
|
|
274
245
|
df = _read_raw_df(file_url)
|
|
275
246
|
if df.empty:
|
|
276
247
|
logger.info(
|
|
277
|
-
f"Anbima TPF secondary market data for {
|
|
248
|
+
f"Anbima TPF secondary market data for {date_str} not available. "
|
|
278
249
|
"Returning empty DataFrame."
|
|
279
250
|
)
|
|
280
251
|
return df
|
|
281
252
|
df = _process_raw_df(df)
|
|
282
|
-
df =
|
|
253
|
+
df = _add_dv01(df)
|
|
283
254
|
df = _add_usd_dv01(df)
|
|
255
|
+
df = _add_di_rate(df)
|
|
284
256
|
df = _reorder_columns(df)
|
|
285
257
|
|
|
286
|
-
if not all_columns:
|
|
287
|
-
df = _select_main_columns(df)
|
|
288
|
-
|
|
289
|
-
if bond_type:
|
|
290
|
-
norm_bond_type = _bond_type_mapping(bond_type) # noqa
|
|
291
|
-
df = df.query("BondType == @norm_bond_type").reset_index(drop=True)
|
|
292
258
|
return df.sort_values(["BondType", "MaturityDate"]).reset_index(drop=True)
|
|
293
259
|
|
|
294
260
|
except HTTPError as e:
|
|
295
261
|
if e.code == 404: # noqa
|
|
296
262
|
logger.info(
|
|
297
|
-
f"No Anbima TPF secondary market data for {
|
|
263
|
+
f"No Anbima TPF secondary market data for {date_str} (HTTP 404). "
|
|
298
264
|
"Returning empty DataFrame."
|
|
299
265
|
)
|
|
300
266
|
return pd.DataFrame()
|
|
301
|
-
logger.error(f"HTTP Error fetching data for {
|
|
267
|
+
logger.error(f"HTTP Error fetching data for {date_str} from {file_url}: {e}")
|
|
302
268
|
raise
|
|
303
269
|
|
|
304
270
|
# Este bloco ainda é útil para outros URLErrors (ex: timeout genuíno na URL pública)
|
|
305
271
|
except URLError:
|
|
306
|
-
logger.exception(f"Network error (URLError) fetching TPF data for {
|
|
272
|
+
logger.exception(f"Network error (URLError) fetching TPF data for {date_str}")
|
|
307
273
|
raise
|
|
308
274
|
|
|
309
275
|
except Exception:
|
|
310
|
-
msg = f"An unexpected error occurred fetching TPF data for {
|
|
276
|
+
msg = f"An unexpected error occurred fetching TPF data for {date_str}"
|
|
311
277
|
logger.exception(msg)
|
|
312
278
|
raise
|
|
313
279
|
|
|
@@ -315,27 +281,27 @@ def fetch_tpf_data(
|
|
|
315
281
|
def tpf_data(
|
|
316
282
|
date: DateScalar,
|
|
317
283
|
bond_type: str | None = None,
|
|
318
|
-
|
|
284
|
+
fetch_from_source: bool = False,
|
|
319
285
|
) -> pd.DataFrame:
|
|
320
|
-
"""Retrieve
|
|
286
|
+
"""Retrieve TPF secondary market data from ANBIMA.
|
|
321
287
|
|
|
322
|
-
This function fetches indicative rates for
|
|
323
|
-
|
|
324
|
-
|
|
288
|
+
This function fetches indicative rates and other data for Brazilian government
|
|
289
|
+
bonds from ANBIMA. By default, it attempts to retrieve data from a local
|
|
290
|
+
cache for performance. If `fetch_from_source` is True, it tries to fetch the data
|
|
291
|
+
directly from the ANBIMA website.
|
|
325
292
|
|
|
326
293
|
Args:
|
|
327
|
-
date (DateScalar): The reference date for the
|
|
328
|
-
bond_type (str, optional):
|
|
329
|
-
Defaults to None,
|
|
330
|
-
|
|
331
|
-
|
|
294
|
+
date (DateScalar): The reference date for the data (e.g., '2024-06-14').
|
|
295
|
+
bond_type (str, optional): Filters results by a specific bond type
|
|
296
|
+
(e.g., 'LTN', 'NTN-B'). Defaults to None, returning all bond types.
|
|
297
|
+
fetch_from_source (bool, optional): If True, forces the function to
|
|
298
|
+
bypass the cache and fetch data directly from the source.
|
|
299
|
+
Defaults to False.
|
|
332
300
|
|
|
333
301
|
Returns:
|
|
334
|
-
pd.DataFrame: A DataFrame
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
- IndicativeRate: The indicative rate of the bond.
|
|
338
|
-
- Price: The price (PU) of the bond.
|
|
302
|
+
pd.DataFrame: A DataFrame containing the requested bond data. An empty
|
|
303
|
+
DataFrame is returned if no data is available for the specified date
|
|
304
|
+
(e.g., weekends, holidays, or future dates).
|
|
339
305
|
|
|
340
306
|
Examples:
|
|
341
307
|
>>> from pyield import anbima
|
|
@@ -347,30 +313,67 @@ def tpf_data(
|
|
|
347
313
|
3 2025-08-22 LFT 2027-03-01 0.000302 17193.200289
|
|
348
314
|
4 2025-08-22 LFT 2027-09-01 0.000411 17186.767105
|
|
349
315
|
...
|
|
316
|
+
|
|
317
|
+
DataFrame columns:
|
|
318
|
+
- BondType: Tipo do título público (e.g., 'LTN', 'NTN-B').
|
|
319
|
+
- ReferenceDate: Data de referência dos dados.
|
|
320
|
+
- SelicCode: Código do título no SELIC.
|
|
321
|
+
- IssueBaseDate: Data base ou de emissão do título.
|
|
322
|
+
- MaturityDate: Data de vencimento do título.
|
|
323
|
+
- BDToMat: Número de dias úteis entre a data de referência e o vencimento.
|
|
324
|
+
- Duration: Macaulay Duration do título em anos.
|
|
325
|
+
- DV01: Variação financeira no preço do título (em BRL) para uma
|
|
326
|
+
mudança de 1 basis point (0,01%) na taxa de juros.
|
|
327
|
+
- DV01USD: O mesmo que DV01, mas convertido para USD pela PTAX do dia.
|
|
328
|
+
- Price: Preço Unitário (PU) do título na data de referência.
|
|
329
|
+
- BidRate: Taxa de compra em formato decimal (e.g., 0.10 para 10%).
|
|
330
|
+
- AskRate: Taxa de venda em formato decimal.
|
|
331
|
+
- IndicativeRate: Taxa indicativa em formato decimal.
|
|
332
|
+
- DIRate: Taxa DI interpolada para o vencimento do título.
|
|
333
|
+
- StdDev: Desvio padrão da taxa indicativa.
|
|
334
|
+
- LowerBoundRateD0: Limite inferior do intervalo indicativo para D+0.
|
|
335
|
+
- UpperBoundRateD0: Limite superior do intervalo indicativo para D+0.
|
|
336
|
+
- LowerBoundRateD1: Limite inferior do intervalo indicativo para D+1.
|
|
337
|
+
- UpperBoundRateD1: Limite superior do intervalo indicativo para D+1.
|
|
338
|
+
- Criteria: Critério utilizado pela ANBIMA para o cálculo.
|
|
339
|
+
|
|
340
|
+
Notes:
|
|
341
|
+
- Data for the last 5 business days is available on the public ANBIMA
|
|
342
|
+
website.
|
|
343
|
+
- Historical data (older than 5 business days) is only available via
|
|
344
|
+
the RTM network. If you are not connected to RTM, requests for
|
|
345
|
+
older dates will return an empty DataFrame.
|
|
350
346
|
"""
|
|
351
|
-
df = get_cached_dataset("tpf")
|
|
352
347
|
date = convert_input_dates(date)
|
|
353
|
-
df = df.query("ReferenceDate == @date").reset_index(drop=True)
|
|
354
348
|
|
|
355
|
-
|
|
349
|
+
date_log = date.strftime("%d/%m/%Y")
|
|
350
|
+
today = dt.datetime.now(BZ_TIMEZONE).date()
|
|
351
|
+
if date.date() > today:
|
|
352
|
+
logger.info(
|
|
353
|
+
f"Cannot fetch data for a future date ({date_log}). "
|
|
354
|
+
"Returning empty DataFrame."
|
|
355
|
+
)
|
|
356
|
+
return pd.DataFrame()
|
|
357
|
+
|
|
358
|
+
if fetch_from_source:
|
|
356
359
|
# Try to fetch the data from the Anbima website
|
|
357
|
-
df =
|
|
360
|
+
df = _fetch_tpf_data(date)
|
|
361
|
+
else:
|
|
362
|
+
df = (
|
|
363
|
+
get_cached_dataset("tpf")
|
|
364
|
+
.query("ReferenceDate == @date")
|
|
365
|
+
.reset_index(drop=True)
|
|
366
|
+
)
|
|
358
367
|
|
|
359
368
|
if df.empty:
|
|
360
369
|
# If the data is still empty, return an empty DataFrame
|
|
361
370
|
return pd.DataFrame()
|
|
362
371
|
|
|
363
372
|
if bond_type:
|
|
364
|
-
|
|
373
|
+
norm_bond_type = _bond_type_mapping(bond_type) # noqa
|
|
374
|
+
df = df.query("BondType == @norm_bond_type").reset_index(drop=True)
|
|
365
375
|
|
|
366
|
-
|
|
367
|
-
df["MaturityDate"] = bday.offset(df["MaturityDate"], 0)
|
|
368
|
-
|
|
369
|
-
return (
|
|
370
|
-
df[["ReferenceDate", "BondType", "MaturityDate", "IndicativeRate", "Price"]]
|
|
371
|
-
.sort_values(["BondType", "MaturityDate"])
|
|
372
|
-
.reset_index(drop=True)
|
|
373
|
-
)
|
|
376
|
+
return df.sort_values(["BondType", "MaturityDate"]).reset_index(drop=True)
|
|
374
377
|
|
|
375
378
|
|
|
376
379
|
def tpf_fixed_rate_maturities(date: DateScalar) -> pd.Series:
|
|
@@ -14,6 +14,41 @@ logger = logging.getLogger(__name__)
|
|
|
14
14
|
TIMEZONE_BZ = ZoneInfo("America/Sao_Paulo")
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
def _get_data(dates: pd.Series) -> pd.DataFrame:
|
|
18
|
+
"""Busca dados de DI, incluindo dados intraday para o dia corrente se necessário."""
|
|
19
|
+
# 1. Busca inicial no cache com as datas solicitadas pelo usuário.
|
|
20
|
+
df_cached = (
|
|
21
|
+
get_cached_dataset("di1").query("TradeDate in @dates").reset_index(drop=True)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
today = dt.now(TIMEZONE_BZ).date()
|
|
25
|
+
last_bday = bday.last_business_day().date()
|
|
26
|
+
|
|
27
|
+
# 2. Lógica para buscar dados intraday.
|
|
28
|
+
# Isso é necessário quando o usuário solicita os dados do dia corrente
|
|
29
|
+
# e eles ainda não foram persistidos no cache (processo noturno).
|
|
30
|
+
if (
|
|
31
|
+
# Condição 1: O dia de hoje foi explicitamente solicitado.
|
|
32
|
+
today in dates.values
|
|
33
|
+
# Condição 2: E ainda não está no cache.
|
|
34
|
+
and today not in df_cached["TradeDate"].values
|
|
35
|
+
# Condição 3: E hoje é o último dia útil.
|
|
36
|
+
and today == last_bday
|
|
37
|
+
):
|
|
38
|
+
try:
|
|
39
|
+
df_intraday = b3.futures(contract_code="DI1", date=today)
|
|
40
|
+
if not df_intraday.empty:
|
|
41
|
+
logger.info("Dados intraday de DI inseridos com sucesso.")
|
|
42
|
+
return pd.concat([df_cached, df_intraday]).reset_index(drop=True)
|
|
43
|
+
else:
|
|
44
|
+
logger.warning(f"Nenhum dado intraday de DI encontrado para {today}.")
|
|
45
|
+
except Exception as e:
|
|
46
|
+
logger.error(f"Falha ao buscar dados intraday para {today}: {e}")
|
|
47
|
+
|
|
48
|
+
# 3. Se a lógica intraday não for acionada ou falhar, retorna apenas dados do cache.
|
|
49
|
+
return df_cached
|
|
50
|
+
|
|
51
|
+
|
|
17
52
|
def data(
|
|
18
53
|
dates: DateScalar | DateArray,
|
|
19
54
|
month_start: bool = False,
|
|
@@ -57,22 +92,14 @@ def data(
|
|
|
57
92
|
"""
|
|
58
93
|
dates = dc.convert_input_dates(dates)
|
|
59
94
|
# Force dates to be a Series for consistency
|
|
60
|
-
|
|
95
|
+
if isinstance(dates, pd.Timestamp):
|
|
96
|
+
dates = pd.Series([dates]).astype("date32[pyarrow]")
|
|
97
|
+
|
|
61
98
|
# Remove duplicates and sort dates
|
|
62
99
|
dates = dates.drop_duplicates().sort_values().reset_index(drop=True)
|
|
63
|
-
|
|
64
|
-
df = get_cached_dataset("di1").query("TradeDate in @dates").reset_index(drop=True)
|
|
65
|
-
|
|
66
|
-
today = dt.now(TIMEZONE_BZ).date()
|
|
67
|
-
last_bday = bday.last_business_day()
|
|
68
|
-
is_trading_day = today == last_bday.date()
|
|
69
|
-
if is_trading_day and last_bday in df["TradeDate"]:
|
|
70
|
-
logger.info("Trying to get DI Futures data for today.")
|
|
71
|
-
df_intraday = b3.futures(contract_code="DI1", date=last_bday)
|
|
72
|
-
df = pd.concat([df, df_intraday]).reset_index(drop=True)
|
|
73
|
-
|
|
100
|
+
df = _get_data(dates)
|
|
74
101
|
if df.empty:
|
|
75
|
-
logger.warning("No
|
|
102
|
+
logger.warning("No DI1 data found for the specified dates.")
|
|
76
103
|
logger.warning("Returning empty DataFrame.")
|
|
77
104
|
return pd.DataFrame()
|
|
78
105
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.37.2"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|