PYield 0.26.2__tar.gz → 0.26.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.
Files changed (36) hide show
  1. {pyield-0.26.2 → pyield-0.26.4}/PKG-INFO +1 -1
  2. pyield-0.26.4/pyield/__about__.py +1 -0
  3. {pyield-0.26.2 → pyield-0.26.4}/pyield/__init__.py +2 -0
  4. {pyield-0.26.2 → pyield-0.26.4}/pyield/bc/auction.py +56 -42
  5. pyield-0.26.2/pyield/__about__.py +0 -1
  6. {pyield-0.26.2 → pyield-0.26.4}/.gitignore +0 -0
  7. {pyield-0.26.2 → pyield-0.26.4}/LICENSE +0 -0
  8. {pyield-0.26.2 → pyield-0.26.4}/README.md +0 -0
  9. {pyield-0.26.2 → pyield-0.26.4}/pyield/anbima.py +0 -0
  10. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/__init__.py +0 -0
  11. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/common.py +0 -0
  12. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/di.py +0 -0
  13. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/historical.py +0 -0
  14. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/intraday.py +0 -0
  15. {pyield-0.26.2 → pyield-0.26.4}/pyield/b3_futures/xml.py +0 -0
  16. {pyield-0.26.2 → pyield-0.26.4}/pyield/bday.py +0 -0
  17. {pyield-0.26.2 → pyield-0.26.4}/pyield/conftest.py +0 -0
  18. {pyield-0.26.2 → pyield-0.26.4}/pyield/data_cache.py +0 -0
  19. {pyield-0.26.2 → pyield-0.26.4}/pyield/date_converter.py +0 -0
  20. {pyield-0.26.2 → pyield-0.26.4}/pyield/holidays/__init__.py +0 -0
  21. {pyield-0.26.2 → pyield-0.26.4}/pyield/holidays/br_holidays_new.txt +0 -0
  22. {pyield-0.26.2 → pyield-0.26.4}/pyield/holidays/br_holidays_old.txt +0 -0
  23. {pyield-0.26.2 → pyield-0.26.4}/pyield/holidays/brholidays.py +0 -0
  24. {pyield-0.26.2 → pyield-0.26.4}/pyield/ima.py +0 -0
  25. {pyield-0.26.2 → pyield-0.26.4}/pyield/indicators.py +0 -0
  26. {pyield-0.26.2 → pyield-0.26.4}/pyield/interpolator.py +0 -0
  27. {pyield-0.26.2 → pyield-0.26.4}/pyield/projections.py +0 -0
  28. {pyield-0.26.2 → pyield-0.26.4}/pyield/py.typed +0 -0
  29. {pyield-0.26.2 → pyield-0.26.4}/pyield/tools.py +0 -0
  30. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/__init__.py +0 -0
  31. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/lft.py +0 -0
  32. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/ltn.py +0 -0
  33. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/ntnb.py +0 -0
  34. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/ntnf.py +0 -0
  35. {pyield-0.26.2 → pyield-0.26.4}/pyield/tpf/tools.py +0 -0
  36. {pyield-0.26.2 → pyield-0.26.4}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PYield
3
- Version: 0.26.2
3
+ Version: 0.26.4
4
4
  Summary: A Python library for analysis of fixed income instruments in Brazil
5
5
  Project-URL: Source, https://github.com/crdcj/PYield
6
6
  Author-email: Carlos Carvalho <cr.cj@outlook.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.26.4"
@@ -2,6 +2,7 @@ from pyield import anbima, bday
2
2
  from pyield.__about__ import __version__
3
3
  from pyield.b3_futures import futures
4
4
  from pyield.b3_futures.di import DIFutures
5
+ from pyield.bc.auction import auctions
5
6
  from pyield.ima import ima
6
7
  from pyield.indicators import indicator
7
8
  from pyield.interpolator import Interpolator
@@ -12,6 +13,7 @@ from pyield.tpf import lft, ltn, ntnb, ntnf
12
13
  __all__ = [
13
14
  "__version__",
14
15
  "anbima",
16
+ "auctions",
15
17
  "bday",
16
18
  "DIFutures",
17
19
  "forward_rates",
@@ -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": "TotalValue", # = FR + SR (in millions)
36
- "quantidadeOfertada": "OfferedQuantity",
37
- "quantidadeAceita": "AcceptedQuantity",
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
- # Calcular o financeiro só do leilão
74
- total_accepted_quantity = df["AcceptedQuantity"] + df["AcceptedQuantitySR"]
75
- first_round_ratio = df["AcceptedQuantity"] / total_accepted_quantity
76
- first_round_ratio = df["AcceptedQuantity"] / total_accepted_quantity
77
- first_round_ratio = first_round_ratio.where(df["AcceptedQuantity"] != 0, 0)
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["Value"] = (first_round_ratio * df["TotalValue"]).round(1)
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
- # Calcular o financeiro só da SV
85
- second_round_ratio = df["AcceptedQuantitySR"] / total_accepted_quantity
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["TotalValue"]).round(1)
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["Value"] / df["AcceptedQuantity"]).round(6)
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["AcceptedQuantity"] != 0
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["AcceptedQuantity"] != 0 # noqa
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"] / 100)
135
- df_ltn["DV01"] = 0.0001 * df_ltn["MDuration"] * df_ltn["AvgPrice"]
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"] / 100)
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"] / 100)
145
- df_ntnf["DV01"] = 0.0001 * df_ntnf["MDuration"] * df_ntnf["AvgPrice"]
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"] / 100)
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"] / 100)
155
- df_ntnb["DV01"] = 0.0001 * df_ntnb["MDuration"] * df_ntnb["AvgPrice"]
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["DV01"] *= df["AcceptedQuantity"]
160
- df["DV01"] = df["DV01"].round(0).astype("Int64")
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
- "DV01",
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 as e:
220
- logging.error(f"Error on fetching auction data from BC: {e}")
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 = "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)?$format=text/csv"
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
- - TotalValue: Total value of the auction: FR + SR.
281
- - Value: First round (FR) value of the auction.
282
- - ValueSR: Value in the second round.
283
- - OfferedQuantity: Quantity offered in the auction.
284
- - OfferedQuantitySR: Quantity offered in the second round.
285
- - AcceptedQuantity: Quantity accepted in the auction.
286
- - AcceptedQuantitySR: Quantity accepted in the second round.
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
- - DV01: Dollar value of a 1 bp change in yield.
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