PYield 0.26.5__tar.gz → 0.26.7__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.26.5 → pyield-0.26.7}/PKG-INFO +1 -1
- pyield-0.26.7/pyield/__about__.py +1 -0
- pyield-0.26.7/pyield/ima.py +156 -0
- pyield-0.26.5/pyield/__about__.py +0 -1
- pyield-0.26.5/pyield/ima.py +0 -143
- {pyield-0.26.5 → pyield-0.26.7}/.gitignore +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/LICENSE +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/README.md +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/__init__.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/anbima.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/__init__.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/common.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/di.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/historical.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/intraday.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/b3_futures/xml.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/bc/auction.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/bday.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/conftest.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/data_cache.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/date_converter.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/holidays/__init__.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/indicators.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/interpolator.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/projections.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/py.typed +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tools.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/__init__.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/lft.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/ltn.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/ntnb.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/ntnf.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyield/tpf/tools.py +0 -0
- {pyield-0.26.5 → pyield-0.26.7}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.26.7"
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import requests
|
|
7
|
+
|
|
8
|
+
ima_types = Literal[
|
|
9
|
+
"IRF-M 1",
|
|
10
|
+
"IRF-M 1+",
|
|
11
|
+
"IRF-M",
|
|
12
|
+
"IMA-B 5",
|
|
13
|
+
"IMA-B 5+",
|
|
14
|
+
"IMA-B",
|
|
15
|
+
"IMA-S",
|
|
16
|
+
"IMA-GERAL-EX-C",
|
|
17
|
+
"IMA-GERAL",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
LAST_IMA_COL_MAPPING = {
|
|
21
|
+
# "2",
|
|
22
|
+
"Data de Referência": "Date",
|
|
23
|
+
"INDICE": "IMAType",
|
|
24
|
+
"Títulos": "BondType",
|
|
25
|
+
"Data de Vencimento": "Maturity",
|
|
26
|
+
"Código SELIC": "SelicCode",
|
|
27
|
+
"Código ISIN": "ISIN",
|
|
28
|
+
"Taxa Indicativa (% a.a.)": "IndicativeRate",
|
|
29
|
+
"PU (R$)": "Price",
|
|
30
|
+
# "PU de Juros (R$)": "InterestPrice",
|
|
31
|
+
"Quantidade (1.000 títulos)": "MarketQuantity",
|
|
32
|
+
"Quantidade Teórica (1.000 títulos)": "TheoreticalQuantity",
|
|
33
|
+
"Carteira a Mercado (R$ mil)": "MarketValue",
|
|
34
|
+
"Peso (%)": "Weight",
|
|
35
|
+
"Prazo (d.u.)": "BDToMat",
|
|
36
|
+
"Duration (d.u.)": "Duration",
|
|
37
|
+
# "Número de Operações *": "NumberOfOperations",
|
|
38
|
+
# "Quant. Negociada (1.000 títulos) *": "NegotiatedQuantity",
|
|
39
|
+
# "Valor Negociado (R$ mil) *": "NegotiatedValue",
|
|
40
|
+
"PMR": "PMR", # Prazmo médio de repactuação
|
|
41
|
+
"Convexidade": "Convexity",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
LAST_IMA_URL = "https://www.anbima.com.br/informacoes/ima/arqs/ima_completo.txt"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _fetch_last_ima() -> pd.DataFrame:
|
|
49
|
+
r = requests.get(LAST_IMA_URL)
|
|
50
|
+
r.raise_for_status()
|
|
51
|
+
r.encoding = "latin1"
|
|
52
|
+
text = r.text.split("2@COMPOSIÇÃO DE CARTEIRA")[1].strip()
|
|
53
|
+
string_io_buffer = io.StringIO(text)
|
|
54
|
+
|
|
55
|
+
df = pd.read_csv(
|
|
56
|
+
string_io_buffer,
|
|
57
|
+
sep="@",
|
|
58
|
+
decimal=",",
|
|
59
|
+
thousands=".",
|
|
60
|
+
dtype_backend="numpy_nullable",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return df
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _process_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
67
|
+
df = df.rename(columns=LAST_IMA_COL_MAPPING)[LAST_IMA_COL_MAPPING.values()]
|
|
68
|
+
df["IndicativeRate"] = (df["IndicativeRate"] / 100).round(6)
|
|
69
|
+
df["Date"] = pd.to_datetime(df["Date"], format="%d/%m/%Y")
|
|
70
|
+
df["Maturity"] = pd.to_datetime(df["Maturity"], format="%d/%m/%Y")
|
|
71
|
+
df["MarketQuantity"] = (1000 * df["MarketQuantity"]).astype("Int64")
|
|
72
|
+
df["Price"] = df["Price"].round(6)
|
|
73
|
+
# Duration is in business days, convert to years
|
|
74
|
+
df["Duration"] /= 252
|
|
75
|
+
mduration = df["Duration"] / (1 + df["IndicativeRate"])
|
|
76
|
+
dv01 = 0.0001 * df["Price"] * mduration * df["MarketQuantity"]
|
|
77
|
+
# Since DV01 and MarketValue are total stock values, we round them to integers
|
|
78
|
+
df["MarketDV01"] = dv01.round(0).astype("Int64")
|
|
79
|
+
df["MarketValue"] = (1000 * df["MarketValue"]).round(0).astype("Int64")
|
|
80
|
+
# LFT DV01 is zero
|
|
81
|
+
df["MarketDV01"] = df["MarketDV01"].where(df["BondType"] != "LFT", 0)
|
|
82
|
+
return df
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _reorder_last_ima(df: pd.DataFrame) -> pd.DataFrame:
|
|
86
|
+
col_order = [
|
|
87
|
+
"Date",
|
|
88
|
+
"IMAType",
|
|
89
|
+
"BondType",
|
|
90
|
+
"Maturity",
|
|
91
|
+
"SelicCode",
|
|
92
|
+
"ISIN",
|
|
93
|
+
"Weight",
|
|
94
|
+
"IndicativeRate",
|
|
95
|
+
"Price",
|
|
96
|
+
"BDToMat",
|
|
97
|
+
"Duration",
|
|
98
|
+
"PMR",
|
|
99
|
+
"Convexity",
|
|
100
|
+
"TheoreticalQuantity",
|
|
101
|
+
"MarketDV01",
|
|
102
|
+
"MarketQuantity",
|
|
103
|
+
"MarketValue",
|
|
104
|
+
]
|
|
105
|
+
return df[col_order].reset_index(drop=True)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def ima(ima_type: ima_types | None = None) -> pd.DataFrame:
|
|
109
|
+
"""
|
|
110
|
+
Fetch and process the last IMA market data available from ANBIMA.
|
|
111
|
+
|
|
112
|
+
This function processes the data into a structured DataFrame.
|
|
113
|
+
It handles conversion of date formats, renames columns to English, and converts
|
|
114
|
+
certain numeric columns to integer types. In the event of an error during data
|
|
115
|
+
fetching or processing, an empty DataFrame is returned.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
ima_type (str, optional): Type of IMA index to filter the data. If None, all
|
|
119
|
+
IMA indexes are returned. Defaults to None.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
pd.DataFrame: A DataFrame containing the IMA data with the following columns:
|
|
123
|
+
- Date: Reference date of the data.
|
|
124
|
+
- Index: IMA index.
|
|
125
|
+
- BondType: Type of bond.
|
|
126
|
+
- Maturity: Bond maturity date.
|
|
127
|
+
- SelicCode: Code representing the SELIC rate.
|
|
128
|
+
- ISIN: International Securities Identification Number.
|
|
129
|
+
- Price: Bond price.
|
|
130
|
+
- Weight: Weight of the bond in the index.
|
|
131
|
+
- BDToMat: Business days to maturity.
|
|
132
|
+
- Duration: Duration of the bond in years.
|
|
133
|
+
- PMR: Average repurchase term.
|
|
134
|
+
- Convexity: Convexity of the bond.
|
|
135
|
+
- TheoreticalQuantity: Theoretical quantity.
|
|
136
|
+
- MarketDV01: Market DV01 in R$.
|
|
137
|
+
- MarketQuantity: Market quantity.
|
|
138
|
+
- MarketValue: Market value in R$.
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
Exception: Logs error and returns an empty DataFrame if any error occurs during
|
|
143
|
+
fetching or processing.
|
|
144
|
+
|
|
145
|
+
"""
|
|
146
|
+
try:
|
|
147
|
+
df = _fetch_last_ima()
|
|
148
|
+
df = _process_last_ima(df)
|
|
149
|
+
df = _reorder_last_ima(df)
|
|
150
|
+
if ima_type is not None:
|
|
151
|
+
df = df.query("IMAType == @ima_type").reset_index(drop=True)
|
|
152
|
+
df = df.sort_values(["IMAType", "BondType", "Maturity"]).reset_index(drop=True)
|
|
153
|
+
return df
|
|
154
|
+
except Exception as e:
|
|
155
|
+
logging.exception(f"Error fetching or processing the last IMA data: {e}")
|
|
156
|
+
return pd.DataFrame()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.26.5"
|
pyield-0.26.5/pyield/ima.py
DELETED
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import io
|
|
2
|
-
import logging
|
|
3
|
-
import re
|
|
4
|
-
import warnings
|
|
5
|
-
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import requests
|
|
8
|
-
from bs4 import XMLParsedAsHTMLWarning
|
|
9
|
-
|
|
10
|
-
from pyield import date_converter as dc
|
|
11
|
-
from pyield.date_converter import DateScalar
|
|
12
|
-
|
|
13
|
-
IMA_URL = "https://www.anbima.com.br/informacoes/ima/ima-quantidade-mercado.asp"
|
|
14
|
-
COLUMN_MAPPING = {
|
|
15
|
-
"Data Referência": "Date",
|
|
16
|
-
"Título": "BondType",
|
|
17
|
-
"Data de Vencimento": "Maturity",
|
|
18
|
-
"Codigo Selic": "SelicCode",
|
|
19
|
-
"Código ISIN": "ISIN",
|
|
20
|
-
"PU (R$)": "Price",
|
|
21
|
-
"Quantidade em Mercado (1.000 Títulos)": "MarketQuantity",
|
|
22
|
-
"Valor de Mercado (R$ Mil)": "MarketValue",
|
|
23
|
-
"Variação da Quantidade (1.000 Títulos)": "QuantityVariation",
|
|
24
|
-
"Status do Titulo": "BondStatus",
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def _fetch_url_tables(target_date: pd.Timestamp) -> pd.DataFrame:
|
|
29
|
-
# Warning supression for BeautifulSoup, as the text is valid HTML and not XML
|
|
30
|
-
warnings.filterwarnings("ignore", category=XMLParsedAsHTMLWarning)
|
|
31
|
-
|
|
32
|
-
target_date_str = target_date.strftime("%d/%m/%Y")
|
|
33
|
-
payload = {
|
|
34
|
-
"Tipo": "",
|
|
35
|
-
"DataRef": "",
|
|
36
|
-
"Pai": "ima",
|
|
37
|
-
"Dt_Ref_Ver": "20250117",
|
|
38
|
-
"Dt_Ref": f"{target_date_str}",
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
r = requests.post(IMA_URL, data=payload, timeout=10)
|
|
42
|
-
r.raise_for_status()
|
|
43
|
-
r.encoding = "iso-8859-1"
|
|
44
|
-
if "Não há dados disponíveis" in r.text:
|
|
45
|
-
logging.warning("No data found. Returning empty DataFrame.")
|
|
46
|
-
return pd.DataFrame()
|
|
47
|
-
|
|
48
|
-
string_io_buffer = io.StringIO(r.text)
|
|
49
|
-
|
|
50
|
-
dfs = pd.read_html(
|
|
51
|
-
string_io_buffer,
|
|
52
|
-
flavor="html5lib",
|
|
53
|
-
attrs={"width": "100%"},
|
|
54
|
-
header=0,
|
|
55
|
-
thousands=".",
|
|
56
|
-
decimal=",",
|
|
57
|
-
displayed_only=True,
|
|
58
|
-
dtype_backend="numpy_nullable",
|
|
59
|
-
)
|
|
60
|
-
|
|
61
|
-
df = (
|
|
62
|
-
pd.concat(dfs)
|
|
63
|
-
.query("`Data de Vencimento`.notnull()")
|
|
64
|
-
.query("Título!='Título'")
|
|
65
|
-
.drop_duplicates(subset="Código ISIN")
|
|
66
|
-
.reset_index(drop=True)
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# Convert to CSV and then back to pandas to get automatic type conversion
|
|
70
|
-
csv_buffer = io.StringIO(df.to_csv(index=False))
|
|
71
|
-
df = pd.read_csv(csv_buffer, dtype_backend="numpy_nullable")
|
|
72
|
-
|
|
73
|
-
# Expressão regular para datas no formato dd/mm/ano
|
|
74
|
-
date_pattern = r"\b(\d{2}/\d{2}/\d{4})\b"
|
|
75
|
-
|
|
76
|
-
# Busca pela primeira data
|
|
77
|
-
match = re.search(date_pattern, r.text)
|
|
78
|
-
|
|
79
|
-
if match:
|
|
80
|
-
anbima_date_str = match.group(1)
|
|
81
|
-
anbima_date = pd.to_datetime(anbima_date_str, format="%d/%m/%Y")
|
|
82
|
-
else:
|
|
83
|
-
logging.warning("No date found. Returning empty DataFrame.")
|
|
84
|
-
return pd.DataFrame()
|
|
85
|
-
|
|
86
|
-
df["Data Referência"] = anbima_date
|
|
87
|
-
|
|
88
|
-
return df
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def _process_df(df: pd.DataFrame) -> pd.DataFrame:
|
|
92
|
-
df = df.rename(columns=COLUMN_MAPPING)[COLUMN_MAPPING.values()]
|
|
93
|
-
df["Maturity"] = pd.to_datetime(df["Maturity"], format="%d/%m/%Y")
|
|
94
|
-
# Converter a valores para inteiros
|
|
95
|
-
df["MarketQuantity"] = (1000 * df["MarketQuantity"]).astype("Int64")
|
|
96
|
-
df["MarketValue"] = (1000 * df["MarketValue"]).astype("Int64")
|
|
97
|
-
df["QuantityVariation"] = (1000 * df["QuantityVariation"]).astype("Int64")
|
|
98
|
-
|
|
99
|
-
return df
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def ima(date: DateScalar) -> pd.DataFrame:
|
|
103
|
-
"""
|
|
104
|
-
Fetch and process IMA market data for a given date.
|
|
105
|
-
|
|
106
|
-
This function retrieves IMA market data for the specified date from ANBIMA,
|
|
107
|
-
processes the data into a structured DataFrame, and returns the resulting DataFrame.
|
|
108
|
-
It handles conversion of date formats, renames columns to English, and converts
|
|
109
|
-
certain numeric columns to integer types. In the event of an error during data
|
|
110
|
-
fetching or processing, an empty DataFrame is returned.
|
|
111
|
-
|
|
112
|
-
Args:
|
|
113
|
-
date (DateScalar): A date-like object representing the target date for fetching
|
|
114
|
-
the data.
|
|
115
|
-
|
|
116
|
-
Returns:
|
|
117
|
-
pd.DataFrame: A DataFrame containing the IMA data with the following columns:
|
|
118
|
-
- Date: Reference date of the data.
|
|
119
|
-
- BondType: Type of bond.
|
|
120
|
-
- Maturity: Bond maturity date.
|
|
121
|
-
- SelicCode: Code representing the SELIC rate.
|
|
122
|
-
- ISIN: International Securities Identification Number.
|
|
123
|
-
- Price: Bond price.
|
|
124
|
-
- MarketQuantity: Market quantity (in units of 1000 bonds).
|
|
125
|
-
- MarketValue: Market value (in units of 1000 reais).
|
|
126
|
-
- QuantityVariation: Variation in quantity (in units of 1000 bonds).
|
|
127
|
-
- BondStatus: Status of the bond.
|
|
128
|
-
|
|
129
|
-
Raises:
|
|
130
|
-
Exception: Logs error and returns an empty DataFrame if any error occurs during
|
|
131
|
-
fetching or processing.
|
|
132
|
-
|
|
133
|
-
"""
|
|
134
|
-
try:
|
|
135
|
-
date = dc.convert_input_dates(date)
|
|
136
|
-
df = _fetch_url_tables(date)
|
|
137
|
-
df = _process_df(df)
|
|
138
|
-
return df
|
|
139
|
-
except Exception as e:
|
|
140
|
-
date_str = date.strftime("%d/%m/%Y")
|
|
141
|
-
logging.error(f"Error fetching IMA for {date_str}. Returning empty DataFrame.")
|
|
142
|
-
logging.error(f"Error message: {e}", exc_info=True)
|
|
143
|
-
return pd.DataFrame()
|
|
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
|