PYield 0.30.2__tar.gz → 0.30.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 (49) hide show
  1. {pyield-0.30.2 → pyield-0.30.4}/PKG-INFO +1 -1
  2. pyield-0.30.4/pyield/__about__.py +1 -0
  3. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/ettj.py +11 -9
  4. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/ima.py +25 -23
  5. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/imaq.py +13 -11
  6. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/ipca.py +6 -6
  7. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/tpf.py +3 -3
  8. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/di.py +25 -18
  9. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/di_over.py +6 -4
  10. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/__init__.py +7 -7
  11. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/bcdata.py +1 -1
  12. pyield-0.30.2/pyield/bc/sec.py → pyield-0.30.4/pyield/bc/trade.py +30 -10
  13. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/ntnf.py +1 -2
  14. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/tools.py +2 -2
  15. pyield-0.30.2/pyield/__about__.py +0 -1
  16. {pyield-0.30.2 → pyield-0.30.4}/.gitignore +0 -0
  17. {pyield-0.30.2 → pyield-0.30.4}/LICENSE +0 -0
  18. {pyield-0.30.2 → pyield-0.30.4}/README.md +0 -0
  19. {pyield-0.30.2 → pyield-0.30.4}/pyield/__init__.py +0 -0
  20. {pyield-0.30.2 → pyield-0.30.4}/pyield/anbima/__init__.py +0 -0
  21. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/__init__.py +0 -0
  22. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/futures/__init__.py +0 -0
  23. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/futures/common.py +0 -0
  24. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/futures/historical.py +0 -0
  25. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/futures/intraday.py +0 -0
  26. {pyield-0.30.2 → pyield-0.30.4}/pyield/b3/futures/xml.py +0 -0
  27. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/auction.py +0 -0
  28. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/ptax.py +0 -0
  29. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/repo.py +0 -0
  30. {pyield-0.30.2 → pyield-0.30.4}/pyield/bc/vna.py +0 -0
  31. {pyield-0.30.2 → pyield-0.30.4}/pyield/bday.py +0 -0
  32. {pyield-0.30.2 → pyield-0.30.4}/pyield/conftest.py +0 -0
  33. {pyield-0.30.2 → pyield-0.30.4}/pyield/data_cache.py +0 -0
  34. {pyield-0.30.2 → pyield-0.30.4}/pyield/date_converter.py +0 -0
  35. {pyield-0.30.2 → pyield-0.30.4}/pyield/forwards.py +0 -0
  36. {pyield-0.30.2 → pyield-0.30.4}/pyield/holidays/__init__.py +0 -0
  37. {pyield-0.30.2 → pyield-0.30.4}/pyield/holidays/br_holidays_new.txt +0 -0
  38. {pyield-0.30.2 → pyield-0.30.4}/pyield/holidays/br_holidays_old.txt +0 -0
  39. {pyield-0.30.2 → pyield-0.30.4}/pyield/holidays/brholidays.py +0 -0
  40. {pyield-0.30.2 → pyield-0.30.4}/pyield/ibge/__init__.py +0 -0
  41. {pyield-0.30.2 → pyield-0.30.4}/pyield/ibge/ipca.py +0 -0
  42. {pyield-0.30.2 → pyield-0.30.4}/pyield/interpolator.py +0 -0
  43. {pyield-0.30.2 → pyield-0.30.4}/pyield/py.typed +0 -0
  44. {pyield-0.30.2 → pyield-0.30.4}/pyield/retry.py +0 -0
  45. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/__init__.py +0 -0
  46. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/lft.py +0 -0
  47. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/ltn.py +0 -0
  48. {pyield-0.30.2 → pyield-0.30.4}/pyield/tpf/ntnb.py +0 -0
  49. {pyield-0.30.2 → pyield-0.30.4}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PYield
3
- Version: 0.30.2
3
+ Version: 0.30.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.30.4"
@@ -117,15 +117,17 @@ def last_ettj() -> pd.DataFrame:
117
117
  calculated and included in the returned DataFrame.
118
118
 
119
119
  Returns:
120
- pd.DataFrame: A DataFrame containing the following columns:
121
- - date: Reference date of the yield curve
122
- - vertex: Time point in business days
123
- - nominal_rate: Zero-coupon nominal interest rate
124
- - real_rate: Zero-coupon real interest rate (IPCA-indexed)
125
- - implied_inflation: Implied inflation rate (break-even inflation)
126
- - nominal_rate_fwd: Forward nominal interest rate
127
- - real_rate_fwd: Forward real interest rate
128
- - implied_inflation_fwd: Forward implied inflation rate
120
+ pd.DataFrame: A DataFrame containing the latest ETTJ data.
121
+
122
+ DataFrame columns:
123
+ - date: Reference date of the yield curve
124
+ - vertex: Time point in business days
125
+ - nominal_rate: Zero-coupon nominal interest rate
126
+ - real_rate: Zero-coupon real interest rate (IPCA-indexed)
127
+ - implied_inflation: Implied inflation rate (break-even inflation)
128
+ - nominal_rate_fwd: Forward nominal interest rate
129
+ - real_rate_fwd: Forward real interest rate
130
+ - implied_inflation_fwd: Forward implied inflation rate
129
131
 
130
132
  Note:
131
133
  All rates are expressed in decimal format (e.g., 0.12 for 12%).
@@ -128,29 +128,31 @@ def last_ima(ima_type: ima_types | None = None) -> pd.DataFrame:
128
128
  IMA indexes are returned. Defaults to None.
129
129
 
130
130
  Returns:
131
- pd.DataFrame: A DataFrame containing the IMA data with the following columns:
132
- - Date: reference date of the data.
133
- - IMAType: type of IMA index.
134
- - BondType: type of bond.
135
- - Maturity: bond maturity date.
136
- - SelicCode: bond code in the SELIC system.
137
- - ISIN: international Securities Identification Number.
138
- - BDToMat: business days to maturity.
139
- - Duration: duration of the bond in business years (252 days/year).
140
- - IndicativeRate: indicative rate.
141
- - Price: bond price.
142
- - InterestPrice: interest price.
143
- - DV01: DV01 in R$.
144
- - PMR: average repurchase term.
145
- - Weight: weight of the bond in the index.
146
- - Convexity: convexity of the bond.
147
- - TheoreticalQuantity: theoretical quantity.
148
- - NumberOfOperations: number of operations.
149
- - NegotiatedQuantity: negotiated quantity.
150
- - NegotiatedValue: negotiated value.
151
- - MarketQuantity: market quantity.
152
- - MarketDV01: market DV01 in R$.
153
- - MarketValue: market value in R$.
131
+ pd.DataFrame: A DataFrame containing the IMA data.
132
+
133
+ DataFrame columns:
134
+ - Date: reference date of the data.
135
+ - IMAType: type of IMA index.
136
+ - BondType: type of bond.
137
+ - Maturity: bond maturity date.
138
+ - SelicCode: bond code in the SELIC system.
139
+ - ISIN: international Securities Identification Number.
140
+ - BDToMat: business days to maturity.
141
+ - Duration: duration of the bond in business years (252 days/year).
142
+ - IndicativeRate: indicative rate.
143
+ - Price: bond price.
144
+ - InterestPrice: interest price.
145
+ - DV01: DV01 in R$.
146
+ - PMR: average repurchase term.
147
+ - Weight: weight of the bond in the index.
148
+ - Convexity: convexity of the bond.
149
+ - TheoreticalQuantity: theoretical quantity.
150
+ - NumberOfOperations: number of operations.
151
+ - NegotiatedQuantity: negotiated quantity.
152
+ - NegotiatedValue: negotiated value.
153
+ - MarketQuantity: market quantity.
154
+ - MarketDV01: market DV01 in R$.
155
+ - MarketValue: market value in R$.
154
156
 
155
157
  Raises:
156
158
  Exception: Logs error and returns an empty DataFrame if any error occurs during
@@ -117,17 +117,19 @@ def imaq(date: DateScalar) -> pd.DataFrame:
117
117
  the data.
118
118
 
119
119
  Returns:
120
- pd.DataFrame: A DataFrame containing the IMA data with the following columns:
121
- - Date: Reference date of the data.
122
- - BondType: Type of bond.
123
- - Maturity: Bond maturity date.
124
- - SelicCode: Code representing the SELIC rate.
125
- - ISIN: International Securities Identification Number.
126
- - Price: Bond price.
127
- - MarketQuantity: Market quantity (in units of 1000 bonds).
128
- - MarketValue: Market value (in units of 1000 reais).
129
- - QuantityVariation: Variation in quantity (in units of 1000 bonds).
130
- - BondStatus: Status of the bond.
120
+ pd.DataFrame: A DataFrame containing the IMA data.
121
+
122
+ DataFrame columns:
123
+ - Date: Reference date of the data.
124
+ - BondType: Type of bond.
125
+ - Maturity: Bond maturity date.
126
+ - SelicCode: Code representing the SELIC rate.
127
+ - ISIN: International Securities Identification Number.
128
+ - Price: Bond price.
129
+ - MarketQuantity: Market quantity (in units of 1000 bonds).
130
+ - MarketValue: Market value (in units of 1000 reais).
131
+ - QuantityVariation: Variation in quantity (in units of 1000 bonds).
132
+ - BondStatus: Status of the bond.
131
133
 
132
134
  Raises:
133
135
  Exception: Logs error and returns an empty DataFrame if any error occurs during
@@ -25,12 +25,12 @@ def ipca_projection() -> IndicatorProjection:
25
25
  containing economic indicators, and specifically processes the IPCA projection data.
26
26
 
27
27
  Process:
28
- 1. Accesses the ANBIMA indicators webpage
29
- 2. Extracts the third table that contains the IPCA projection
30
- 3. Locates the row labeled as "IPCA1"
31
- 4. Extracts the projection value and converts it to decimal format
32
- 5. Extracts and formats the reference month of the projection
33
- 6. Extracts the date and time of the last update
28
+ 1. Accesses the ANBIMA indicators webpage
29
+ 2. Extracts the third table that contains the IPCA projection
30
+ 3. Locates the row labeled as "IPCA1"
31
+ 4. Extracts the projection value and converts it to decimal format
32
+ 5. Extracts and formats the reference month of the projection
33
+ 6. Extracts the date and time of the last update
34
34
 
35
35
  Returns:
36
36
  IndicatorProjection: An object containing:
@@ -78,7 +78,7 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
78
78
  return df.sort_values(["BondType", "MaturityDate"], ignore_index=True)
79
79
 
80
80
 
81
- def tpf_data(date: DateScalar, bond_type: str | None = None) -> pd.DataFrame:
81
+ def tpf_data(date: DateScalar, bond_type: BOND_TYPES | None = None) -> pd.DataFrame:
82
82
  """Fetch and process TPF secondary market data from ANBIMA.
83
83
 
84
84
  This function retrieves bond market data from the ANBIMA website for a
@@ -109,8 +109,8 @@ def tpf_data(date: DateScalar, bond_type: str | None = None) -> pd.DataFrame:
109
109
  return df
110
110
  df = _process_raw_df(df)
111
111
  if bond_type:
112
- bond_type = _bond_type_mapping(bond_type)
113
- df = df.query("BondType == @bond_type").reset_index(drop=True)
112
+ norm_bond_type = _bond_type_mapping(bond_type) # noqa
113
+ df = df.query("BondType == @norm_bond_type").reset_index(drop=True)
114
114
  return df.sort_values(["BondType", "MaturityDate"]).reset_index(drop=True)
115
115
  except HTTPError as e:
116
116
  if e.code == 404: # noqa
@@ -1,3 +1,5 @@
1
+ import logging
2
+
1
3
  import pandas as pd
2
4
 
3
5
  import pyield.date_converter as dc
@@ -6,6 +8,8 @@ from pyield import b3, bday, interpolator
6
8
  from pyield.data_cache import get_anbima_dataset, get_di_dataset
7
9
  from pyield.date_converter import DateArray, DateScalar
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
 
10
14
  class DIFutures:
11
15
  """
@@ -61,7 +65,7 @@ class DIFutures:
61
65
 
62
66
  def __init__(
63
67
  self,
64
- date: DateScalar | None = None,
68
+ date: DateScalar,
65
69
  month_start: bool = False,
66
70
  pre_filter: bool = False,
67
71
  all_columns: bool = True,
@@ -100,19 +104,22 @@ class DIFutures:
100
104
 
101
105
  def _update_df(self) -> pd.DataFrame:
102
106
  """Retrieve DI contract DataFrame for the initialized trade date."""
103
- bz_today = pd.Timestamp.today(tz="America/Sao_Paulo").normalize()
104
- if bz_today == self.date:
105
- # Try to get intraday data
106
- df = b3.futures(contract_code="DI1", date=bz_today)
107
- else:
108
- # Get historical data
109
- df = (
110
- get_di_dataset()
111
- .query("TradeDate == @self._date")
112
- .reset_index(drop=True)
113
- )
107
+ # Return an empty DataFrame if the trade date is a holiday
108
+ if not bday.is_business_day(self._date):
109
+ logger.warning("Specified date is not a business day.")
110
+ logger.warning("Returning empty DataFrame.")
111
+ return pd.DataFrame()
112
+
113
+ # Get historical data
114
+ df = get_di_dataset().query("TradeDate == @self._date").reset_index(drop=True)
115
+
116
+ if df.empty:
117
+ logger.info("No historical data found. Trying real-time data.")
118
+ df = b3.futures(contract_code="DI1", date=self._date)
114
119
 
115
120
  if df.empty:
121
+ logger.warning("No DI Futures data found for the specified date.")
122
+ logger.warning("Returning empty DataFrame.")
116
123
  return pd.DataFrame()
117
124
 
118
125
  if "DaysToExpiration" in df.columns:
@@ -349,12 +356,12 @@ class DIFutures:
349
356
  return self._date
350
357
 
351
358
  @date.setter
352
- def date(self, value: DateScalar | None):
353
- if value:
354
- self._date = dc.convert_input_dates(value)
355
- else:
356
- self._date = self.historical_dates.max()
357
-
359
+ def date(self, value: DateScalar):
360
+ self._date = dc.convert_input_dates(value)
361
+ bz_today = pd.Timestamp.today(tz="America/Sao_Paulo")
362
+ bz_today = bz_today.normalize().tz_localize(None)
363
+ if self._date > bz_today:
364
+ raise ValueError("Trade date cannot be in the future.")
358
365
  self._dirty = True
359
366
 
360
367
  @property
@@ -5,6 +5,9 @@ from pyield.date_converter import DateScalar, convert_input_dates
5
5
 
6
6
  logger = logging.getLogger(__name__)
7
7
 
8
+ # 4 decimal places in rate = 2 decimal places in percentage
9
+ DI_OVER_DECIMAL_PLACES = 4
10
+
8
11
 
9
12
  def di_over(date: DateScalar) -> float:
10
13
  """
@@ -14,7 +17,7 @@ def di_over(date: DateScalar) -> float:
14
17
  date (str): Date in DD/MM/YYYY format
15
18
 
16
19
  Returns:
17
- float: DI rate for the specified date
20
+ float: DI rate for the specified date or NaN (Not a Number) if no data is found.
18
21
 
19
22
  Raises:
20
23
  ValueError: If date is not in the correct format
@@ -47,7 +50,7 @@ def di_over(date: DateScalar) -> float:
47
50
  if lines:
48
51
  raw_rate = lines[0].strip()
49
52
  rate = int(raw_rate) / 10000
50
- return round(rate, 5)
53
+ return round(rate, DI_OVER_DECIMAL_PLACES)
51
54
  else:
52
55
  logger.error(f"No data found for date {date}")
53
56
  return float("nan")
@@ -62,8 +65,7 @@ def di_over(date: DateScalar) -> float:
62
65
  logger.exception("Unexpected error")
63
66
  raise
64
67
  finally:
65
- # Ensure FTP connection is closed
66
- if ftp:
68
+ if ftp and ftp.sock: # Ensure FTP connection is closed AND socket exists
67
69
  try:
68
70
  ftp.quit()
69
71
  except: # noqa
@@ -9,19 +9,19 @@ from pyield.bc.bcdata import (
9
9
  )
10
10
  from pyield.bc.ptax import ptax_series
11
11
  from pyield.bc.repo import repos
12
- from pyield.bc.sec import sec
12
+ from pyield.bc.trade import sec
13
13
  from pyield.bc.vna import vna_lft
14
14
 
15
15
  __all__ = [
16
16
  "auctions",
17
- "repos",
18
- "ptax_series",
19
- "vna_lft",
20
- "selic_over_series",
21
- "selic_target_series",
17
+ "di_over",
22
18
  "di_over_series",
19
+ "ptax_series",
20
+ "repos",
23
21
  "sec",
24
- "di_over",
25
22
  "selic_over",
23
+ "selic_over_series",
26
24
  "selic_target",
25
+ "selic_target_series",
26
+ "vna_lft",
27
27
  ]
@@ -146,7 +146,7 @@ def selic_over_series(
146
146
  Date Value
147
147
  0 2024-05-31 0.104
148
148
 
149
- # No data on 26-01-2025 (sunday). Rate changed due to Copom meeting.
149
+ >>> # No data on 26-01-2025 (sunday). Rate changed due to Copom meeting.
150
150
  >>> bc.selic_over_series("26-01-2025") # Returns all data since 26-01-2025
151
151
  Date Value
152
152
  0 2025-01-27 0.1215
@@ -62,22 +62,42 @@ def _fetch_data_from_url(file_url: str) -> pd.DataFrame:
62
62
 
63
63
 
64
64
  def sec(target_date: DateScalar) -> pd.DataFrame:
65
- """Fetches bond trading data for a specific date from the Brazilian Central Bank.
65
+ """Fetches secondary market trading data for all Federal Public Securities (TPF)
66
+ registered on SELIC for the *month* of the specified date.
66
67
 
67
- Downloads the daily bond trading data from the Brazilian Central Bank
68
- website for the given target date. The data is downloaded as a ZIP file,
69
- extracted, and read into a Pandas DataFrame. The columns are then
70
- renamed to English names for easier use.
68
+ Downloads the monthly bond trading data from the Brazilian Central Bank (BCB)
69
+ website for the month corresponding to the provided date. The data is downloaded as
70
+ a ZIP file, extracted, and loaded into a Pandas DataFrame.
71
+ The data contains all trades executed during the month, separated by settlement day
72
+ ('SettlementDate').
71
73
 
72
74
  Args:
73
- target_date (DateScalar): The date for which to fetch the bond
74
- trading data. This can be a date-like object understood by
75
- `pyield.date_converter.convert_input_dates`.
75
+ target_date (DateScalar): The date for which to fetch the bond trading data.
76
+ This can be any date-like object understood by
77
+ `pyield.date_converter.convert_input_dates`. Only the year and month
78
+ of this date will be used to download the corresponding monthly file.
76
79
 
77
80
  Returns:
78
81
  pd.DataFrame: A DataFrame containing the bond trading data for the
79
- specified date. Columns are renamed according to `COLUMN_MAPPING`
80
- for English readability.
82
+ specified month.
83
+
84
+ DataFrame columns:
85
+ - SettlementDate: Date when the trade settled
86
+ - BondType: Security type abbreviation
87
+ - SelicCode: Unique code in the SELIC system
88
+ - ISIN: International Securities Identification Number
89
+ - IssueDate: Date when the security was issued
90
+ - Maturity: Security's maturity date
91
+ - TradeCount: Number of trades executed
92
+ - TradeQuantity: Quantity traded
93
+ - TradeValue: Total value traded
94
+ - AvgPrice: Average price
95
+ - AvgRate: Average rate
96
+ And additional trading metrics like min/max prices and rates.
97
+
98
+ Examples:
99
+ >>> from pyield import bc
100
+ >>> df = bc.sec("07-01-2025") # Returns all trades for January 2025
81
101
  """
82
102
  url = _build_download_url(target_date)
83
103
  df = _fetch_data_from_url(url)
@@ -258,7 +258,6 @@ def spot_rates( # noqa
258
258
  "BDToMat" is the business days from the settlement date to the maturities.
259
259
 
260
260
  Examples:
261
-
262
261
  >>> df_ltn = yd.ltn.rates("03-09-2024")
263
262
  >>> df_ntnf = yd.ntnf.rates("03-09-2024")
264
263
  >>> yd.ntnf.spot_rates(
@@ -522,7 +521,7 @@ def premium(
522
521
  factor.
523
522
 
524
523
  Examples:
525
- # Obs: only some of the DI rates will be used in the example.
524
+ >>> # Obs: only some of the DI rates will be used in the example.
526
525
  >>> exp_dates = pd.to_datetime(["2025-01-01", "2030-01-01", "2035-01-01"])
527
526
  >>> di_rates = pd.Series([0.10823, 0.11594, 0.11531])
528
527
  >>> premium(
@@ -79,8 +79,8 @@ def pre_spreads(date: DateScalar) -> pd.DataFrame:
79
79
  converted_date = dc.convert_input_dates(date)
80
80
  di = DIFutures(date=converted_date, month_start=True)
81
81
  df_di = di.df
82
- if "SettlementRate" not in df_di.columns:
83
- raise ValueError("DI rates data is missing the 'SettlementRate' column.")
82
+ if "SettlementRate" not in df_di.columns or df_di.empty:
83
+ raise ValueError("DI data is missing the 'SettlementRate' column or is empty.")
84
84
 
85
85
  df_di = df_di[["ExpirationDate", "SettlementRate"]].copy()
86
86
 
@@ -1 +0,0 @@
1
- __version__ = "0.30.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