PYield 0.26.2__tar.gz → 0.26.3__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.2 → pyield-0.26.3}/PKG-INFO +1 -1
- pyield-0.26.3/pyield/__about__.py +1 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/bc/auction.py +56 -42
- pyield-0.26.2/pyield/__about__.py +0 -1
- {pyield-0.26.2 → pyield-0.26.3}/.gitignore +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/LICENSE +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/README.md +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/__init__.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/anbima.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/__init__.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/common.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/di.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/historical.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/intraday.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/b3_futures/xml.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/bday.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/conftest.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/data_cache.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/date_converter.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/holidays/__init__.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/holidays/brholidays.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/ima.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/indicators.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/interpolator.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/projections.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/py.typed +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tools.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/__init__.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/lft.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/ltn.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/ntnb.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/ntnf.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyield/tpf/tools.py +0 -0
- {pyield-0.26.2 → pyield-0.26.3}/pyproject.toml +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.26.3"
|
|
@@ -32,9 +32,9 @@ COLUMN_MAPPING = {
|
|
|
32
32
|
# "prazo": "...", # N. de DC entre a data de liquidação e a data de vencimento
|
|
33
33
|
"codigoTitulo": "SelicCode", # [100000, 210100, 450000, 760199, 950199]
|
|
34
34
|
"dataVencimento": "Maturity",
|
|
35
|
-
"financeiro": "
|
|
36
|
-
"quantidadeOfertada": "
|
|
37
|
-
"quantidadeAceita": "
|
|
35
|
+
"financeiro": "Value", # = FR + SR (in millions)
|
|
36
|
+
"quantidadeOfertada": "OfferedQuantityFR",
|
|
37
|
+
"quantidadeAceita": "AcceptedQuantityFR",
|
|
38
38
|
"quantidadeOfertadaSegundaRodada": "OfferedQuantitySR",
|
|
39
39
|
"quantidadeAceitaSegundaRodada": "AcceptedQuantitySR",
|
|
40
40
|
"cotacaoMedia": "AvgPrice",
|
|
@@ -43,6 +43,8 @@ COLUMN_MAPPING = {
|
|
|
43
43
|
"taxaCorte": "CutRate",
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
BASE_API_URL = "https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/odata/leiloesTitulosPublicos(dataMovimentoInicio=@dataMovimentoInicio,dataMovimentoFim=@dataMovimentoFim,dataLiquidacao=@dataLiquidacao,codigoTitulo=@codigoTitulo,dataVencimento=@dataVencimento,edital=@edital,tipoPublico=@tipoPublico,tipoOferta=@tipoOferta)?"
|
|
47
|
+
|
|
46
48
|
|
|
47
49
|
def _load_from_url(url: str) -> pd.DataFrame:
|
|
48
50
|
response = requests.get(url, timeout=10)
|
|
@@ -70,22 +72,28 @@ def _process_df(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
70
72
|
df["AcceptedQuantitySR"] = df["AcceptedQuantitySR"].fillna(0)
|
|
71
73
|
df["OfferedQuantitySR"] = df["OfferedQuantitySR"].fillna(0)
|
|
72
74
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
df["OfferedQuantity"] = df["OfferedQuantityFR"] + df["OfferedQuantitySR"]
|
|
76
|
+
df["AcceptedQuantity"] = df["AcceptedQuantityFR"] + df["AcceptedQuantitySR"]
|
|
77
|
+
# Total value of the auction in R$ millions -> convert it to unit value
|
|
78
|
+
df["Value"] = (1_000_000 * df["Value"]).round(0).astype("Int64")
|
|
79
|
+
|
|
80
|
+
# Remove the percentage sign and round to 6 decimal places (4 decimal places in %)
|
|
81
|
+
# Before 25/08/2015, there were no rounding rules for the rates in the BC API
|
|
82
|
+
df["AvgRate"] = (df["AvgRate"] / 100).round(6)
|
|
83
|
+
df["CutRate"] = (df["CutRate"] / 100).round(6)
|
|
84
|
+
|
|
85
|
+
# Calculate the financial value of the first round
|
|
86
|
+
first_round_ratio = df["AcceptedQuantityFR"] / df["AcceptedQuantity"]
|
|
87
|
+
# Force 0 when AcceptedQuantityFR is 0 to avoid division by zero
|
|
88
|
+
first_round_ratio = first_round_ratio.where(df["AcceptedQuantityFR"] != 0, 0)
|
|
78
89
|
# O dado do financeiro do BC está em milhões com uma casa decimal de precisão
|
|
79
90
|
# Portanto, podemos converter para inteiro sem perda de informação
|
|
80
|
-
df["
|
|
81
|
-
df["Value"] = (first_round_ratio * df["TotalValue"]).round(1)
|
|
82
|
-
df["Value"] = (df["Value"] * 1_000_000).astype("Int64")
|
|
91
|
+
df["ValueFR"] = (first_round_ratio * df["Value"]).round(0).astype("Int64")
|
|
83
92
|
|
|
84
|
-
#
|
|
85
|
-
second_round_ratio = df["AcceptedQuantitySR"] /
|
|
93
|
+
# Calculate the financial value of the second round
|
|
94
|
+
second_round_ratio = df["AcceptedQuantitySR"] / df["AcceptedQuantity"]
|
|
86
95
|
second_round_ratio = second_round_ratio.where(df["AcceptedQuantitySR"] != 0, 0)
|
|
87
|
-
df["ValueSR"] = (second_round_ratio * df["
|
|
88
|
-
df["ValueSR"] = (df["ValueSR"] * 1_000_000).astype("Int64")
|
|
96
|
+
df["ValueSR"] = (second_round_ratio * df["Value"]).round(0).astype("Int64")
|
|
89
97
|
|
|
90
98
|
bond_mappping = {
|
|
91
99
|
100000: "LTN",
|
|
@@ -99,7 +107,7 @@ def _process_df(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
99
107
|
# Em 11/06/2024 o BC passou a informar nas colunas de cotacao os valores dos PUs
|
|
100
108
|
# Isso afeta somente os títulos LFT e NTN-B
|
|
101
109
|
change_Date = pd.Timestamp("2024-06-11")
|
|
102
|
-
adjusted_price = (df["
|
|
110
|
+
adjusted_price = (df["ValueFR"] / df["AcceptedQuantityFR"]).round(6)
|
|
103
111
|
is_after_change_Date = df["Date"] >= change_Date
|
|
104
112
|
is_ltn_or_ntnf = df["BondType"].isin(["LTN", "NTN-F"])
|
|
105
113
|
keep_avg_price = is_after_change_Date | is_ltn_or_ntnf
|
|
@@ -110,7 +118,7 @@ def _process_df(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
110
118
|
|
|
111
119
|
def _adjust_null_values(df: pd.DataFrame) -> pd.DataFrame:
|
|
112
120
|
# Onde não há quantidade aceita, não há nem taxa e nem PU definidos.
|
|
113
|
-
is_accepted = df["
|
|
121
|
+
is_accepted = df["AcceptedQuantityFR"] != 0
|
|
114
122
|
df["AvgRate"] = df["AvgRate"].where(is_accepted, pd.NA)
|
|
115
123
|
df["CutRate"] = df["CutRate"].where(is_accepted, pd.NA)
|
|
116
124
|
df["AvgPrice"] = df["AvgPrice"].where(is_accepted, pd.NA)
|
|
@@ -122,7 +130,7 @@ def _adjust_null_values(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
122
130
|
def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
123
131
|
df["BDToMat"] = yd.bday.count(start=df["Date"], end=df["Maturity"])
|
|
124
132
|
|
|
125
|
-
is_accepted = df["
|
|
133
|
+
is_accepted = df["AcceptedQuantityFR"] != 0 # noqa
|
|
126
134
|
df_not_accepted = df.query("~@is_accepted").reset_index(drop=True)
|
|
127
135
|
df_is_accepted = df.query("@is_accepted").reset_index(drop=True)
|
|
128
136
|
|
|
@@ -131,33 +139,33 @@ def _add_dv01(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
131
139
|
df_ltn = df_is_accepted.query("BondType == 'LTN'").reset_index(drop=True)
|
|
132
140
|
if not df_ltn.empty:
|
|
133
141
|
df_ltn["Duration"] = df_ltn["BDToMat"] / 252
|
|
134
|
-
df_ltn["MDuration"] = df_ltn["Duration"] / (1 + df_ltn["AvgRate"]
|
|
135
|
-
df_ltn["
|
|
142
|
+
df_ltn["MDuration"] = df_ltn["Duration"] / (1 + df_ltn["AvgRate"])
|
|
143
|
+
df_ltn["DV01FR"] = 0.0001 * df_ltn["MDuration"] * df_ltn["AvgPrice"]
|
|
136
144
|
|
|
137
145
|
def compute_f_duration(row):
|
|
138
|
-
return duration_f(row["Date"], row["Maturity"], row["CutRate"]
|
|
146
|
+
return duration_f(row["Date"], row["Maturity"], row["CutRate"])
|
|
139
147
|
|
|
140
148
|
df_ntnf = df_is_accepted.query("BondType == 'NTN-F'").reset_index(drop=True)
|
|
141
149
|
if not df_ntnf.empty:
|
|
142
150
|
df_ntnf["Duration"] = df_ntnf.apply(compute_f_duration, axis=1)
|
|
143
151
|
df_ntnf["Duration"] = df_ntnf["Duration"].astype("Float64")
|
|
144
|
-
df_ntnf["MDuration"] = df_ntnf["Duration"] / (1 + df_ntnf["AvgRate"]
|
|
145
|
-
df_ntnf["
|
|
152
|
+
df_ntnf["MDuration"] = df_ntnf["Duration"] / (1 + df_ntnf["AvgRate"])
|
|
153
|
+
df_ntnf["DV01FR"] = 0.0001 * df_ntnf["MDuration"] * df_ntnf["AvgPrice"]
|
|
146
154
|
|
|
147
155
|
def compute_b_duration(row):
|
|
148
|
-
return duration_b(row["Date"], row["Maturity"], row["CutRate"]
|
|
156
|
+
return duration_b(row["Date"], row["Maturity"], row["CutRate"])
|
|
149
157
|
|
|
150
158
|
df_ntnb = df_is_accepted.query("BondType == 'NTN-B'").reset_index(drop=True)
|
|
151
159
|
if not df_ntnb.empty:
|
|
152
160
|
df_ntnb["Duration"] = df_ntnb.apply(compute_b_duration, axis=1)
|
|
153
161
|
df_ntnb["Duration"] = df_ntnb["Duration"].astype("Float64")
|
|
154
|
-
df_ntnb["MDuration"] = df_ntnb["Duration"] / (1 + df_ntnb["AvgRate"]
|
|
155
|
-
df_ntnb["
|
|
162
|
+
df_ntnb["MDuration"] = df_ntnb["Duration"] / (1 + df_ntnb["AvgRate"])
|
|
163
|
+
df_ntnb["DV01FR"] = 0.0001 * df_ntnb["MDuration"] * df_ntnb["AvgPrice"]
|
|
156
164
|
|
|
157
165
|
df = pd.concat([df_not_accepted, df_lft, df_ltn, df_ntnf, df_ntnb])
|
|
158
166
|
|
|
159
|
-
df["
|
|
160
|
-
df["
|
|
167
|
+
df["DV01FR"] *= df["AcceptedQuantityFR"]
|
|
168
|
+
df["DV01FR"] = df["DV01FR"].round(0).astype("Int64")
|
|
161
169
|
|
|
162
170
|
return df
|
|
163
171
|
|
|
@@ -182,12 +190,14 @@ def _sort_and_reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
182
190
|
"BondType",
|
|
183
191
|
"Maturity",
|
|
184
192
|
"SelicCode",
|
|
185
|
-
"TotalValue",
|
|
186
193
|
"Value",
|
|
194
|
+
"ValueFR",
|
|
187
195
|
"ValueSR",
|
|
188
196
|
"OfferedQuantity",
|
|
197
|
+
"OfferedQuantityFR",
|
|
189
198
|
"OfferedQuantitySR",
|
|
190
199
|
"AcceptedQuantity",
|
|
200
|
+
"AcceptedQuantityFR",
|
|
191
201
|
"AcceptedQuantitySR",
|
|
192
202
|
"AvgPrice",
|
|
193
203
|
"CutPrice",
|
|
@@ -196,7 +206,7 @@ def _sort_and_reorder_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
196
206
|
"BDToMat",
|
|
197
207
|
"Duration",
|
|
198
208
|
"MDuration",
|
|
199
|
-
"
|
|
209
|
+
"DV01FR",
|
|
200
210
|
]
|
|
201
211
|
|
|
202
212
|
primary_sort_keys = ["Date", "AuctionType", "BondType", "Maturity"]
|
|
@@ -216,8 +226,8 @@ def _fetch_df_from_url(
|
|
|
216
226
|
df = _filter_auction_by_type(df, auction_type)
|
|
217
227
|
df = _sort_and_reorder_columns(df)
|
|
218
228
|
return df
|
|
219
|
-
except Exception
|
|
220
|
-
logging.
|
|
229
|
+
except Exception:
|
|
230
|
+
logging.exception("Error fetching auction data from BC API.")
|
|
221
231
|
return pd.DataFrame()
|
|
222
232
|
|
|
223
233
|
|
|
@@ -234,7 +244,8 @@ def get_all_auctions() -> pd.DataFrame:
|
|
|
234
244
|
DataFrame is returned and an error message is logged.
|
|
235
245
|
"""
|
|
236
246
|
# URL com toda a série de leilões de títulos públicos do Tesouro Nacional
|
|
237
|
-
url = "
|
|
247
|
+
url = f"{BASE_API_URL}$format=text/csv"
|
|
248
|
+
|
|
238
249
|
return _fetch_df_from_url(url)
|
|
239
250
|
|
|
240
251
|
|
|
@@ -277,13 +288,15 @@ def auctions(
|
|
|
277
288
|
- BondType: Category of the bond (e.g., "LTN", "LFT", "NTN-B", "NTN-F").
|
|
278
289
|
- Maturity: Bond maturity date.
|
|
279
290
|
- SelicCode: Bond code in the Selic system.
|
|
280
|
-
-
|
|
281
|
-
-
|
|
282
|
-
- ValueSR: Value in the second round
|
|
283
|
-
- OfferedQuantity:
|
|
284
|
-
-
|
|
285
|
-
-
|
|
286
|
-
-
|
|
291
|
+
- Value: Total value of the auction in R$ (FR + SR).
|
|
292
|
+
- ValueFR: First round (FR) value of the auction in R$.
|
|
293
|
+
- ValueSR: Value in the second round (SR) in R$.
|
|
294
|
+
- OfferedQuantity: Total quantity offered in the auction (FR + SR).
|
|
295
|
+
- OfferedQuantityFR: Quantity offered in the first round (FR).
|
|
296
|
+
- OfferedQuantitySR: Quantity offered in the second round (SR).
|
|
297
|
+
- AcceptedQuantity: Total quantity accepted in the auction (FR + SR).
|
|
298
|
+
- AcceptedQuantityFR: Quantity accepted in the first round (FR).
|
|
299
|
+
- AcceptedQuantitySR: Quantity accepted in the second round (SR).
|
|
287
300
|
- AvgPrice: Average price in the auction.
|
|
288
301
|
- CutPrice: Cut-off price.
|
|
289
302
|
- AvgRate: Average interest rate.
|
|
@@ -291,7 +304,7 @@ def auctions(
|
|
|
291
304
|
- BDToMat: Business days to maturity.
|
|
292
305
|
- Duration: Duration of the bond.
|
|
293
306
|
- MDuration: Modified duration of the bond.
|
|
294
|
-
-
|
|
307
|
+
- DV01FR: First Round (FR) dollar value of a 1 bp change in yield in R$.
|
|
295
308
|
|
|
296
309
|
"""
|
|
297
310
|
|
|
@@ -303,6 +316,7 @@ def auctions(
|
|
|
303
316
|
|
|
304
317
|
start_str = start.strftime("%Y-%m-%d")
|
|
305
318
|
end_str = end.strftime("%Y-%m-%d")
|
|
306
|
-
url = f"https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/odata/leiloesTitulosPublicos(dataMovimentoInicio=@dataMovimentoInicio,dataMovimentoFim=@dataMovimentoFim,dataLiquidacao=@dataLiquidacao,codigoTitulo=@codigoTitulo,dataVencimento=@dataVencimento,edital=@edital,tipoPublico=@tipoPublico,tipoOferta=@tipoOferta)?@dataMovimentoInicio='{start_str}'&@dataMovimentoFim='{end_str}'&$format=text/csv"
|
|
319
|
+
# url = f"https://olinda.bcb.gov.br/olinda/servico/leiloes_selic/versao/v1/odata/leiloesTitulosPublicos(dataMovimentoInicio=@dataMovimentoInicio,dataMovimentoFim=@dataMovimentoFim,dataLiquidacao=@dataLiquidacao,codigoTitulo=@codigoTitulo,dataVencimento=@dataVencimento,edital=@edital,tipoPublico=@tipoPublico,tipoOferta=@tipoOferta)?@dataMovimentoInicio='{start_str}'&@dataMovimentoFim='{end_str}'&$format=text/csv"
|
|
320
|
+
url = f"{BASE_API_URL}@dataMovimentoInicio='{start_str}'&@dataMovimentoFim='{end_str}'&$format=text/csv" # noqa
|
|
307
321
|
|
|
308
322
|
return _fetch_df_from_url(url, auction_type)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.26.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
|