PYield 0.7.3__tar.gz → 0.7.5__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 (30) hide show
  1. {pyield-0.7.3 → pyield-0.7.5}/PKG-INFO +53 -26
  2. {pyield-0.7.3 → pyield-0.7.5}/README.md +51 -25
  3. pyield-0.7.5/pyield/__about__.py +1 -0
  4. {pyield-0.7.3 → pyield-0.7.5}/pyield/__init__.py +2 -1
  5. pyield-0.7.5/pyield/data_access.py +148 -0
  6. pyield-0.7.5/pyield/futures/__init__.py +13 -0
  7. {pyield-0.7.3 → pyield-0.7.5}/pyield/futures/common.py +7 -5
  8. {pyield-0.7.3 → pyield-0.7.5}/pyield/futures/ddi.py +7 -44
  9. {pyield-0.7.3 → pyield-0.7.5}/pyield/futures/di.py +19 -38
  10. pyield-0.7.5/pyield/futures/frc.py +98 -0
  11. {pyield-0.7.3 → pyield-0.7.5}/pyield/indicators.py +58 -4
  12. pyield-0.7.5/pyield/projections.py +87 -0
  13. {pyield-0.7.3 → pyield-0.7.5}/pyield/treasuries.py +1 -1
  14. {pyield-0.7.3 → pyield-0.7.5}/pyield/utils.py +10 -10
  15. {pyield-0.7.3 → pyield-0.7.5}/pyproject.toml +2 -1
  16. {pyield-0.7.3 → pyield-0.7.5}/tests/test_futures.py +5 -5
  17. pyield-0.7.3/pyield/__about__.py +0 -1
  18. pyield-0.7.3/pyield/data_access.py +0 -95
  19. pyield-0.7.3/pyield/futures/__init__.py +0 -11
  20. {pyield-0.7.3 → pyield-0.7.5}/LICENSE +0 -0
  21. {pyield-0.7.3 → pyield-0.7.5}/pyield/bday.py +0 -0
  22. {pyield-0.7.3 → pyield-0.7.5}/pyield/data_analysis.py +0 -0
  23. {pyield-0.7.3 → pyield-0.7.5}/pyield/futures/di_xml.py +0 -0
  24. {pyield-0.7.3 → pyield-0.7.5}/pyield/holidays/__init__.py +0 -0
  25. {pyield-0.7.3 → pyield-0.7.5}/pyield/holidays/br_holidays_new.txt +0 -0
  26. {pyield-0.7.3 → pyield-0.7.5}/pyield/holidays/br_holidays_old.txt +0 -0
  27. {pyield-0.7.3 → pyield-0.7.5}/pyield/holidays/core.py +0 -0
  28. {pyield-0.7.3 → pyield-0.7.5}/pyield/py.typed +0 -0
  29. {pyield-0.7.3 → pyield-0.7.5}/tests/__init__.py +0 -0
  30. {pyield-0.7.3 → pyield-0.7.5}/tests/test_bday.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.7.3
3
+ Version: 0.7.5
4
4
  Summary: A Python library for analysis of fixed income instruments in Brazil
5
5
  Keywords: fixed-income, brazil, finance, analysis, bonds
6
6
  Author-Email: Carlos Carvalho <cr.cj@outlook.com>
@@ -38,13 +38,14 @@ Requires-Dist: numpy
38
38
  Requires-Dist: beautifulsoup4
39
39
  Requires-Dist: html5lib
40
40
  Requires-Dist: lxml
41
+ Requires-Dist: python-calamine>=0.2.0
41
42
  Description-Content-Type: text/markdown
42
43
 
43
44
  [![PyPI version](https://img.shields.io/pypi/v/pyield.svg)](https://pypi.python.org/pypi/pyield)
44
45
  [![Made with Python](https://img.shields.io/badge/Python->=3.11-blue?logo=python&logoColor=white)](https://python.org "Go to Python homepage")
45
46
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license)
46
47
 
47
- # PYield: Fixed Income Toolbox for Brazilian Markets
48
+ # PYield: Brazilian Fixed Income Toolkit
48
49
 
49
50
  ## Introduction
50
51
 
@@ -98,17 +99,27 @@ dtype: datetime64[ns]
98
99
 
99
100
  ### Futures Data
100
101
  ```python
101
- # Fetch a DataFrame with the DI Futures data from B3
102
+ # Fetch current DI Futures data from B3 (15 minutes delay)
103
+ >>> yd.fetch_asset(asset_code="DI1")
104
+ TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate LastRate
105
+ 2024-04-21 13:37:39 DI1K24 2024-05-02 7 ... 0.10660 0.10652 0.10660 0.10660
106
+ 2024-04-21 13:37:39 DI1M24 2024-06-03 28 ... 0.10518 0.10510 0.10516 0.10518
107
+ 2024-04-21 13:37:39 DI1N24 2024-07-01 48 ... 0.10480 0.10456 0.10462 0.10460
108
+ ... ... ... ... ... ... ... ... ...
109
+ 2024-04-21 13:37:39 DI1F37 2037-01-02 3183 ... <NA> <NA> 0.11600 <NA>
110
+ 2024-04-21 13:37:39 DI1F38 2038-01-04 3432 ... <NA> <NA> 0.11600 <NA>
111
+ 2024-04-21 13:37:39 DI1F39 2039-01-03 3683 ... <NA> <NA> <NA> <NA>
112
+
113
+ # Fetch historical DI Futures data from B3
102
114
  >>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
103
-
104
115
  TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastBidRate SettlementRate
105
- 2024-03-08 DI1J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
106
- 2024-03-08 DI1K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
107
- 2024-03-08 DI1M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
108
- ... ... ... ... ... ... ... ... ...
109
- 2024-03-08 DI1F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
110
- 2024-03-08 DI1F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
111
- 2024-03-08 DI1F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
116
+ 2024-03-08 DI1J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
117
+ 2024-03-08 DI1K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
118
+ 2024-03-08 DI1M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
119
+ ... ... ... ... ... ... ... ... ...
120
+ 2024-03-08 DI1F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
121
+ 2024-03-08 DI1F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
122
+ 2024-03-08 DI1F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
112
123
  ```
113
124
 
114
125
  ### Treasury Bonds Data
@@ -118,14 +129,14 @@ TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastB
118
129
  # Obs: Anbima members have access to the full history
119
130
  >>> yd.fetch_asset(asset_code="NTN-B", reference_date='2024-04-12')
120
131
 
121
- BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
122
- NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
123
- NTN-B 2024-04-12 2025-05-15 0.05945 0.05913 0.05930 4,361.34391
124
- NTN-B 2024-04-12 2026-08-15 0.05927 0.05897 0.05910 4,301.40082
125
- ... ... ... ... ... ... ...
126
- NTN-B 2024-04-12 2050-08-15 0.06039 0.06006 0.06023 4,299.28233
127
- NTN-B 2024-04-12 2055-05-15 0.06035 0.05998 0.06017 4,367.13360
128
- NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
132
+ BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
133
+ NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
134
+ NTN-B 2024-04-12 2025-05-15 0.05945 0.05913 0.05930 4,361.34391
135
+ NTN-B 2024-04-12 2026-08-15 0.05927 0.05897 0.05910 4,301.40082
136
+ ... ... ... ... ... ... ...
137
+ NTN-B 2024-04-12 2050-08-15 0.06039 0.06006 0.06023 4,299.28233
138
+ NTN-B 2024-04-12 2055-05-15 0.06035 0.05998 0.06017 4,367.13360
139
+ NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
129
140
  ```
130
141
 
131
142
  ### Spreads Calculation
@@ -134,13 +145,13 @@ NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
134
145
  >>> yd.calculate_spreads(spread_type="di_vs_pre", reference_date="2024-4-11")
135
146
 
136
147
  BondType ReferenceDate MaturityDate DISpread
137
- LTN 2024-04-11 2024-07-01 -20.28000
138
- LTN 2024-04-11 2024-10-01 -10.19000
139
- LTN 2024-04-11 2025-01-01 -15.05000
140
- ... ... ... ...
141
- NTN-F 2024-04-11 2031-01-01 -0.66000
142
- NTN-F 2024-04-11 2033-01-01 -5.69000
143
- NTN-F 2024-04-11 2035-01-01 -1.27000
148
+ LTN 2024-04-11 2024-07-01 -20.28
149
+ LTN 2024-04-11 2024-10-01 -10.19
150
+ LTN 2024-04-11 2025-01-01 -15.05
151
+ ... ... ... ...
152
+ NTN-F 2024-04-11 2031-01-01 -0.66
153
+ NTN-F 2024-04-11 2033-01-01 -5.69
154
+ NTN-F 2024-04-11 2035-01-01 -1.27
144
155
  ```
145
156
 
146
157
  ### Indicators Data
@@ -157,3 +168,19 @@ NTN-F 2024-04-11 2035-01-01 -1.27000
157
168
  >>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
158
169
  None
159
170
  ```
171
+
172
+ ### Projections Data
173
+ ```python
174
+ # Fetch current month projection for IPCA from IBGE API
175
+ >>> ipca = yd.fetch_projection(projection_code="IPCA_CM")
176
+ >>> print(ipca)
177
+ IndicatorProjection(
178
+ last_updated=Timestamp('2024-04-19 18:55:00'),
179
+ reference_month_ts=Timestamp('2024-04-01 00:00:00'),
180
+ reference_month_br='ABR/2024',
181
+ projected_value=0.0035
182
+ )
183
+ >>> ipca.projected_value
184
+ 0.0035
185
+ ```
186
+
@@ -2,7 +2,7 @@
2
2
  [![Made with Python](https://img.shields.io/badge/Python->=3.11-blue?logo=python&logoColor=white)](https://python.org "Go to Python homepage")
3
3
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license)
4
4
 
5
- # PYield: Fixed Income Toolbox for Brazilian Markets
5
+ # PYield: Brazilian Fixed Income Toolkit
6
6
 
7
7
  ## Introduction
8
8
 
@@ -56,17 +56,27 @@ dtype: datetime64[ns]
56
56
 
57
57
  ### Futures Data
58
58
  ```python
59
- # Fetch a DataFrame with the DI Futures data from B3
59
+ # Fetch current DI Futures data from B3 (15 minutes delay)
60
+ >>> yd.fetch_asset(asset_code="DI1")
61
+ TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate LastRate
62
+ 2024-04-21 13:37:39 DI1K24 2024-05-02 7 ... 0.10660 0.10652 0.10660 0.10660
63
+ 2024-04-21 13:37:39 DI1M24 2024-06-03 28 ... 0.10518 0.10510 0.10516 0.10518
64
+ 2024-04-21 13:37:39 DI1N24 2024-07-01 48 ... 0.10480 0.10456 0.10462 0.10460
65
+ ... ... ... ... ... ... ... ... ...
66
+ 2024-04-21 13:37:39 DI1F37 2037-01-02 3183 ... <NA> <NA> 0.11600 <NA>
67
+ 2024-04-21 13:37:39 DI1F38 2038-01-04 3432 ... <NA> <NA> 0.11600 <NA>
68
+ 2024-04-21 13:37:39 DI1F39 2039-01-03 3683 ... <NA> <NA> <NA> <NA>
69
+
70
+ # Fetch historical DI Futures data from B3
60
71
  >>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
61
-
62
72
  TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastBidRate SettlementRate
63
- 2024-03-08 DI1J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
64
- 2024-03-08 DI1K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
65
- 2024-03-08 DI1M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
66
- ... ... ... ... ... ... ... ... ...
67
- 2024-03-08 DI1F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
68
- 2024-03-08 DI1F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
69
- 2024-03-08 DI1F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
73
+ 2024-03-08 DI1J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
74
+ 2024-03-08 DI1K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
75
+ 2024-03-08 DI1M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
76
+ ... ... ... ... ... ... ... ... ...
77
+ 2024-03-08 DI1F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
78
+ 2024-03-08 DI1F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
79
+ 2024-03-08 DI1F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
70
80
  ```
71
81
 
72
82
  ### Treasury Bonds Data
@@ -76,14 +86,14 @@ TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastB
76
86
  # Obs: Anbima members have access to the full history
77
87
  >>> yd.fetch_asset(asset_code="NTN-B", reference_date='2024-04-12')
78
88
 
79
- BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
80
- NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
81
- NTN-B 2024-04-12 2025-05-15 0.05945 0.05913 0.05930 4,361.34391
82
- NTN-B 2024-04-12 2026-08-15 0.05927 0.05897 0.05910 4,301.40082
83
- ... ... ... ... ... ... ...
84
- NTN-B 2024-04-12 2050-08-15 0.06039 0.06006 0.06023 4,299.28233
85
- NTN-B 2024-04-12 2055-05-15 0.06035 0.05998 0.06017 4,367.13360
86
- NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
89
+ BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
90
+ NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
91
+ NTN-B 2024-04-12 2025-05-15 0.05945 0.05913 0.05930 4,361.34391
92
+ NTN-B 2024-04-12 2026-08-15 0.05927 0.05897 0.05910 4,301.40082
93
+ ... ... ... ... ... ... ...
94
+ NTN-B 2024-04-12 2050-08-15 0.06039 0.06006 0.06023 4,299.28233
95
+ NTN-B 2024-04-12 2055-05-15 0.06035 0.05998 0.06017 4,367.13360
96
+ NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
87
97
  ```
88
98
 
89
99
  ### Spreads Calculation
@@ -92,13 +102,13 @@ NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
92
102
  >>> yd.calculate_spreads(spread_type="di_vs_pre", reference_date="2024-4-11")
93
103
 
94
104
  BondType ReferenceDate MaturityDate DISpread
95
- LTN 2024-04-11 2024-07-01 -20.28000
96
- LTN 2024-04-11 2024-10-01 -10.19000
97
- LTN 2024-04-11 2025-01-01 -15.05000
98
- ... ... ... ...
99
- NTN-F 2024-04-11 2031-01-01 -0.66000
100
- NTN-F 2024-04-11 2033-01-01 -5.69000
101
- NTN-F 2024-04-11 2035-01-01 -1.27000
105
+ LTN 2024-04-11 2024-07-01 -20.28
106
+ LTN 2024-04-11 2024-10-01 -10.19
107
+ LTN 2024-04-11 2025-01-01 -15.05
108
+ ... ... ... ...
109
+ NTN-F 2024-04-11 2031-01-01 -0.66
110
+ NTN-F 2024-04-11 2033-01-01 -5.69
111
+ NTN-F 2024-04-11 2035-01-01 -1.27
102
112
  ```
103
113
 
104
114
  ### Indicators Data
@@ -115,3 +125,19 @@ NTN-F 2024-04-11 2035-01-01 -1.27000
115
125
  >>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
116
126
  None
117
127
  ```
128
+
129
+ ### Projections Data
130
+ ```python
131
+ # Fetch current month projection for IPCA from IBGE API
132
+ >>> ipca = yd.fetch_projection(projection_code="IPCA_CM")
133
+ >>> print(ipca)
134
+ IndicatorProjection(
135
+ last_updated=Timestamp('2024-04-19 18:55:00'),
136
+ reference_month_ts=Timestamp('2024-04-01 00:00:00'),
137
+ reference_month_br='ABR/2024',
138
+ projected_value=0.0035
139
+ )
140
+ >>> ipca.projected_value
141
+ 0.0035
142
+ ```
143
+
@@ -0,0 +1 @@
1
+ __version__ = "0.7.5"
@@ -1,6 +1,6 @@
1
1
  from .__about__ import __version__
2
2
  from .bday import count_bdays, generate_bdays, is_bday, offset_bdays
3
- from .data_access import fetch_asset, fetch_indicator
3
+ from .data_access import fetch_asset, fetch_indicator, fetch_projection
4
4
  from .data_analysis import calculate_spreads
5
5
 
6
6
  __all__ = [
@@ -11,5 +11,6 @@ __all__ = [
11
11
  "count_bdays",
12
12
  "fetch_asset",
13
13
  "fetch_indicator",
14
+ "fetch_projection",
14
15
  "calculate_spreads",
15
16
  ]
@@ -0,0 +1,148 @@
1
+ import pandas as pd
2
+
3
+ from . import futures as ft
4
+ from . import indicators as it
5
+ from . import projections as pr
6
+ from . import treasuries as tr
7
+ from .utils import _normalize_date
8
+
9
+
10
+ def fetch_asset(
11
+ asset_code: str,
12
+ reference_date: str | pd.Timestamp | None = None,
13
+ **kwargs,
14
+ ) -> pd.DataFrame:
15
+ """
16
+ Fetches data for a specified asset based on type and reference date.
17
+
18
+ Args:
19
+ asset_code (str): The asset code identifying the type of financial asset.
20
+ Supported options:
21
+ - "TRB": Treasury bonds (indicative rates from ANBIMA).
22
+ - "LTN", "LFT", "NTN-F", "NTN-B": Specific types of Brazilian treasury bonds
23
+ (indicative rates from ANBIMA).
24
+ - "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
25
+ - "DDI": DI x U.S. Dollar Spread Futures (Futuro de Cupom Cambial) from B3.
26
+ - "FRC": Forward Rate Agreement (FRA) from B3.
27
+ reference_date (str | pd.Timestamp | None): The reference date for which data is
28
+ fetched. Defaults to the last business day if None.
29
+ **kwargs: Additional keyword arguments, specifically:
30
+ - return_raw (bool): Whether to return raw data without processing. Defaults
31
+ to False.
32
+
33
+ Returns:
34
+ pd.DataFrame: A DataFrame containing the fetched data for the specified asset.
35
+
36
+ Raises:
37
+ ValueError: If the asset code is not recognized or supported.
38
+
39
+ Examples:
40
+ >>> fetch_asset('TRB', '2023-04-01')
41
+ >>> fetch_asset('DI1', '2023-04-01', return_raw=True)
42
+ """
43
+ return_raw = kwargs.get("return_raw", False)
44
+ normalized_date = _normalize_date(reference_date)
45
+
46
+ if asset_code.lower() == "trb":
47
+ return tr.fetch_bonds(reference_date=normalized_date, return_raw=return_raw)
48
+ elif asset_code.lower() in ["ltn", "lft", "ntn-f", "ntn-b"]:
49
+ df = tr.fetch_bonds(reference_date=normalized_date)
50
+ return df.query(f"BondType == '{asset_code.upper()}'")
51
+
52
+ elif asset_code.lower() == "di1":
53
+ today = pd.Timestamp.today().normalize()
54
+ if normalized_date == today:
55
+ return ft.fetch_last_di()
56
+ else:
57
+ return ft.fetch_past_di(trade_date=normalized_date, return_raw=return_raw)
58
+ elif asset_code.lower() == "ddi":
59
+ return ft.fetch_past_ddi(trade_date=normalized_date, return_raw=return_raw)
60
+ elif asset_code.lower() == "frc":
61
+ return ft.fetch_past_frc(trade_date=normalized_date, return_raw=return_raw)
62
+ else:
63
+ raise ValueError("Asset type not supported.")
64
+
65
+
66
+ def fetch_indicator(
67
+ indicator_code: str,
68
+ reference_date: str | pd.Timestamp | None = None,
69
+ ) -> float | None:
70
+ """
71
+ Fetches data for a specified economic indicator and reference date.
72
+
73
+ Args:
74
+ indicator_code (str): The code for the economic indicator. Supported options:
75
+ - "SELIC": SELIC target rate from the Central Bank of Brazil, expressed as
76
+ an annual rate (p.a.).
77
+ - "DI": Interbank Deposit rate (DI) from B3 expressed as a daily rate,
78
+ calculated on a daily basis and expressed per diem (p.d.).
79
+ - "IPCA": IPCA monthly inflation rate from IBGE, expressed per month (p.m.).
80
+ - "VNA_LFT": VNA (Valor Nominal Atualizado) of LFT (Letra Financeira do
81
+ Tesouro), which reflects updated nominal values for these bonds.
82
+ - reference_date (str | pd.Timestamp | None): The reference date for which data
83
+ is fetched. Defaults to the last business day if None.
84
+
85
+ Returns:
86
+ float | None: The value of the specified economic indicator for the reference
87
+ date. Returns None if data is not found.
88
+
89
+ Raises:
90
+ ValueError: If the indicator code is not recognized or supported.
91
+
92
+ Examples:
93
+ >>> fetch_indicator('SELIC', '2023-04-01')
94
+ 0.1075 # Indicates a SELIC target rate of 10.75% p.a.
95
+ >>> fetch_indicator('IPCA', '2023-03-10')
96
+ 0.0016 # Indicates an IPCA monthly rate of 0.16% p.m.
97
+ >>> fetch_indicator('DI', '2023-04-17')
98
+ 0.00040168 # Indicates a DI daily rate of 0.02% p.d.
99
+ """
100
+ normalized_date = _normalize_date(reference_date)
101
+
102
+ if indicator_code.lower() == "selic":
103
+ return it.fetch_selic_target(reference_date=normalized_date)
104
+ elif indicator_code.lower() == "ipca":
105
+ return it.fetch_ipca_mr(reference_date=normalized_date)
106
+ elif indicator_code.lower() == "di":
107
+ return it.fetch_di(reference_date=normalized_date)
108
+ elif indicator_code.lower() == "vna_lft":
109
+ return it.fetch_vna_selic(reference_date=normalized_date)
110
+ else:
111
+ raise ValueError("Indicator type not supported.")
112
+
113
+
114
+ def fetch_projection(projection_code: str) -> dict:
115
+ """
116
+ Fetches a financial projection for a specified code and reference date.
117
+
118
+ Args:
119
+ projection_code (str): The code for the financial projection. Supported options:
120
+ - "IPCA_CM": IPCA projection for the current month from ANBIMA.
121
+
122
+ Returns:
123
+ IndicatorProjection: An instance of IndicatorProjection containing:
124
+ - last_updated (pd.Timestamp): The datetime when the projection was last
125
+ updated.
126
+ - reference_month_ts (pd.Timestamp): The month to which the projection
127
+ applies.
128
+ - reference_month_br (str): The formatted month as a string formatted using
129
+ the pt_BR locale.
130
+ - projected_value (float): The projected indicator value.
131
+
132
+ Raises:
133
+ ValueError: If the projection code is not recognized or supported.
134
+
135
+ Examples:
136
+ >>> fetch_projection('IPCA_CM')
137
+ IndicatorProjection(
138
+ last_updated=pd.Timestamp('2024-04-19 18:55:00'),
139
+ reference_month_ts=pd.Timestamp('2024-04-01 00:00:00'),
140
+ reference_month_br='ABR/2024',
141
+ projected_value=0.35
142
+ )
143
+ """
144
+
145
+ if projection_code.lower() == "ipca_cm":
146
+ return pr.fetch_current_month_ipca_projection()
147
+ else:
148
+ raise ValueError("Projection type not supported.")
@@ -0,0 +1,13 @@
1
+ from .common import get_expiration_date, get_old_expiration_date
2
+ from .ddi import fetch_past_ddi
3
+ from .di import fetch_last_di, fetch_past_di
4
+ from .frc import fetch_past_frc
5
+
6
+ __all__ = [
7
+ "fetch_past_di",
8
+ "fetch_last_di",
9
+ "fetch_past_ddi",
10
+ "fetch_past_frc",
11
+ "get_expiration_date",
12
+ "get_old_expiration_date",
13
+ ]
@@ -131,9 +131,10 @@ def get_old_expiration_date(
131
131
  return pd.NaT # type: ignore
132
132
 
133
133
 
134
- def fetch_past_data(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
134
+ def fetch_past_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
135
135
  """
136
- Fetch the historical futures data from B3 for a specific trade date.
136
+ Fetch the historical futures data from B3 for a specific trade date. If the data is
137
+ not available, an empty DataFrame is returned.
137
138
 
138
139
  Args:
139
140
  trade_date (pd.Timestamp): The trade date for which the data should be fetched.
@@ -148,7 +149,7 @@ def fetch_past_data(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
148
149
 
149
150
  text = r.text
150
151
  if "AJUSTE" not in text:
151
- raise ValueError(f"Could not fetch data for {url_date}.")
152
+ return pd.DataFrame()
152
153
 
153
154
  df = pd.read_html(
154
155
  io.StringIO(text),
@@ -170,12 +171,13 @@ def fetch_past_data(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
170
171
  df["VAR. PTOS."] = df["VAR. PTOS."].astype(pd.StringDtype())
171
172
 
172
173
  # Force "AJUSTE CORRIG. (4)" to be float, since it can be also read as int
173
- df["AJUSTE CORRIG. (4)"] = df["AJUSTE CORRIG. (4)"].astype(pd.Float64Dtype())
174
+ if "AJUSTE CORRIG. (4)" in df.columns:
175
+ df["AJUSTE CORRIG. (4)"] = df["AJUSTE CORRIG. (4)"].astype(pd.Float64Dtype())
174
176
 
175
177
  return df
176
178
 
177
179
 
178
- def fetch_last_data(future_code: str) -> pd.DataFrame:
180
+ def fetch_last_raw_df(future_code: str) -> pd.DataFrame:
179
181
  """
180
182
  Fetch the latest data for a given future code from B3 derivatives quotation API.
181
183
 
@@ -20,26 +20,6 @@ def _convert_prices_to_rates(prices: pd.Series, n_days: pd.Series) -> pd.Series:
20
20
  return 100 * rates
21
21
 
22
22
 
23
- def _convert_prices_in_older_contracts(df: pd.DataFrame) -> pd.DataFrame:
24
- # Prior to 01/01/2002, prices were not converted to rates
25
- convert_cols = [
26
- "FirstRate",
27
- "MinRate",
28
- "MaxRate",
29
- "AvgRate",
30
- "LastRate",
31
- "LastBidRate",
32
- "LastAskRate",
33
- ]
34
- for col in convert_cols:
35
- df[col] = _convert_prices_to_rates(df[col], df["DaysToExpiration"])
36
-
37
- # Invert low and high prices
38
- df["MinRate"], df["MaxRate"] = df["MaxRate"], df["MinRate"]
39
-
40
- return df
41
-
42
-
43
23
  def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
44
24
  """
45
25
  Internal function to process and transform raw DI futures data.
@@ -96,33 +76,16 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
96
76
  # Remove expired contracts
97
77
  df.query("DaysToExpiration > 0", inplace=True)
98
78
 
99
- # Columns where 0 means NaN
100
- cols_with_nan = [
101
- "SettlementPrice",
102
- "FirstRate",
103
- "MinRate",
104
- "MaxRate",
105
- "AvgRate",
106
- "LastRate",
107
- "LastBidRate",
108
- "LastAskRate",
109
- ]
110
- for col in cols_with_nan:
111
- df[col] = df[col].replace(0, pd.NA)
112
-
113
- # Prior to 17/01/2002 (incluive), prices were not converted to rates
114
- if trade_date <= pd.Timestamp("2002-01-17"):
115
- df = _convert_prices_in_older_contracts(df)
116
-
117
79
  df["SettlementRate"] = _convert_prices_to_rates(
118
80
  df["SettlementPrice"], df["DaysToExpiration"]
119
81
  )
120
82
 
121
- # Remove percentage in all rate columns and round to 5 decimal places since it's the
122
- # precision used by B3. Obs: 5 decimal places = 3 decimal places in percentage
123
83
  rate_cols = [col for col in df.columns if "Rate" in col]
124
- for col in rate_cols:
125
- df[col] = (df[col] / 100).round(5)
84
+ cols_with_nan = rate_cols + ["SettlementPrice"]
85
+ # Columns where 0 means NaN
86
+ df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
87
+ # Remove % and round to 5 dec. places (3 in %) since it is the contract's precision
88
+ df[rate_cols] = df[rate_cols].div(100).round(5)
126
89
 
127
90
  # Filter and order columns
128
91
  ordered_cols = [
@@ -149,7 +112,7 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
149
112
  return df[ordered_cols]
150
113
 
151
114
 
152
- def fetch_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
115
+ def fetch_past_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
153
116
  """
154
117
  Fetchs the DDI futures data for a given date from B3.
155
118
 
@@ -172,7 +135,7 @@ def fetch_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFram
172
135
  - DaysToExpiration: number of business days to ExpirationDate.
173
136
  - OpenContracts: number of open contracts at the start of the trading day.
174
137
  """
175
- df_raw = common.fetch_past_data(asset_code="DDI", trade_date=trade_date)
138
+ df_raw = common.fetch_past_raw_df(asset_code="DDI", trade_date=trade_date)
176
139
  if return_raw:
177
140
  return df_raw
178
141
  return _process_raw_df(df_raw, trade_date)
@@ -41,7 +41,7 @@ def _convert_prices_in_older_contracts(df: pd.DataFrame) -> pd.DataFrame:
41
41
  return df
42
42
 
43
43
 
44
- def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
44
+ def _process_past_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
45
45
  """
46
46
  Internal function to process and transform raw DI futures data.
47
47
 
@@ -52,10 +52,6 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
52
52
  Returns:
53
53
  pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
54
54
  """
55
- # Check if the pd.DataFrame is empty
56
- if df.empty:
57
- return df
58
-
59
55
  rename_dict = {
60
56
  "VENCTO": "ExpirationCode",
61
57
  "CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
@@ -95,20 +91,6 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
95
91
  # Remove expired contracts
96
92
  df.query("BDaysToExp > 0", inplace=True)
97
93
 
98
- # Columns where 0 means NaN
99
- cols_with_nan = [
100
- "SettlementPrice",
101
- "FirstRate",
102
- "MinRate",
103
- "MaxRate",
104
- "AvgRate",
105
- "CloseRate",
106
- "CloseBidRate",
107
- "CloseAskRate",
108
- ]
109
- for col in cols_with_nan:
110
- df[col] = df[col].replace(0, pd.NA)
111
-
112
94
  # Prior to 17/01/2002 (incluive), prices were not converted to rates
113
95
  if trade_date <= pd.Timestamp("2002-01-17"):
114
96
  df = _convert_prices_in_older_contracts(df)
@@ -117,11 +99,12 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
117
99
  df["SettlementPrice"], df["BDaysToExp"]
118
100
  )
119
101
 
120
- # Remove percentage in all rate columns and round to 5 decimal places since it's the
121
- # precision used by B3. Obs: 5 decimal places = 3 decimal places in percentage
122
102
  rate_cols = [col for col in df.columns if "Rate" in col]
123
- for col in rate_cols:
124
- df[col] = (df[col] / 100).round(5)
103
+ cols_with_nan = rate_cols + ["SettlementPrice"]
104
+ # Columns where 0 means NaN
105
+ df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
106
+ # Remove % and round to 5 dec. places (3 in %) since it is the contract's precision
107
+ df[rate_cols] = df[rate_cols].div(100).round(5)
125
108
 
126
109
  df["TickerSymbol"] = "DI1" + df["ExpirationCode"]
127
110
 
@@ -151,7 +134,7 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
151
134
  return df[ordered_cols]
152
135
 
153
136
 
154
- def _process_last_di_data(raw_df: pd.DataFrame) -> pd.DataFrame:
137
+ def _process_last_raw_di_df(raw_df: pd.DataFrame) -> pd.DataFrame:
155
138
  df = raw_df.copy()
156
139
 
157
140
  # Columns to be renamed
@@ -188,43 +171,41 @@ def _process_last_di_data(raw_df: pd.DataFrame) -> pd.DataFrame:
188
171
  return df[rename_columns.values()]
189
172
 
190
173
 
191
- def fetch_last_di_data() -> pd.DataFrame:
174
+ def fetch_last_di() -> pd.DataFrame:
192
175
  """
193
176
  Fetch the latest DI futures data from B3.
194
177
 
195
178
  Returns:
196
179
  pd.DataFrame: A Pandas pd.DataFrame containing the latest DI futures data.
197
180
  """
198
- raw_df = common.fetch_last_data(future_code="DI1")
199
- return _process_last_di_data(raw_df)
181
+ raw_df = common.fetch_last_raw_df(future_code="DI1")
182
+ return _process_last_raw_di_df(raw_df)
200
183
 
201
184
 
202
- def fetch_past_di_data(
203
- trade_date: pd.Timestamp, return_raw: bool = False
204
- ) -> pd.DataFrame:
185
+ def fetch_past_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
205
186
  """
206
187
  Fetchs the DI futures data for a given date from B3.
207
188
 
208
189
  This function fetches and processes the DI futures data from B3 for a specific
209
- trade date. It's the primary external interface for accessing DI data.
190
+ trade date. It's the primary external interface for accessing DI data. If the data
191
+ is not available, an empty DataFrame is returned.
210
192
 
211
193
  Args:
212
194
  trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
213
- raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
195
+ return_raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
214
196
  Defaults to False.
215
197
 
216
198
  Returns:
217
199
  pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
218
200
 
219
- Examples:
220
- >>> from pyield.futures import di
221
- >>> di.fetch_di(pd.Timestamp("2021-01-04"))
222
-
223
201
  Notes:
224
202
  - BDaysToExp: number of business days to ExpirationDate.
225
203
  - OpenContracts: number of open contracts at the start of the trading day.
226
204
  """
227
- df_raw = common.fetch_past_data(asset_code="DI1", trade_date=trade_date)
205
+ df_raw = common.fetch_past_raw_df(asset_code="DI1", trade_date=trade_date)
228
206
  if return_raw:
229
207
  return df_raw
230
- return _process_past_data(df_raw, trade_date)
208
+ if df_raw.empty:
209
+ return pd.DataFrame()
210
+ else:
211
+ return _process_past_raw_df(df_raw, trade_date)
@@ -0,0 +1,98 @@
1
+ import pandas as pd
2
+
3
+ from . import common
4
+
5
+
6
+ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
7
+ """
8
+ Internal function to process and transform raw DI futures data.
9
+
10
+ Args:
11
+ df (pd.DataFrame): the raw DI DataFrame.
12
+ trade_date: a datetime-like object representing the trade date.
13
+
14
+ Returns:
15
+ pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
16
+ """
17
+ # Check if the pd.DataFrame is empty
18
+ if df.empty:
19
+ return df
20
+
21
+ rename_dict = {
22
+ "VENCTO": "ExpirationCode",
23
+ "NÚM. NEGOC.": "TradeCount",
24
+ "CONTR. NEGOC.": "TradeVolume",
25
+ "VOL.": "FinancialVolume",
26
+ "AJUSTE DE REF.": "SettlementRate",
27
+ "PREÇO ABERTU.": "FirstRate",
28
+ "PREÇO MÍN.": "MinRate",
29
+ "PREÇO MÉD.": "AvgRate",
30
+ "PREÇO MÁX.": "MaxRate",
31
+ "ÚLT. PREÇO": "CloseRate",
32
+ "VAR. PTOS.": "PointsVariation",
33
+ # Attention: bid/ask rates are inverted
34
+ "ÚLT.OF. COMPRA": "CloseAskRate",
35
+ "ÚLT.OF. VENDA": "CloseBidRate",
36
+ }
37
+
38
+ df = df.rename(columns=rename_dict)
39
+
40
+ df["TradeDate"] = trade_date
41
+ # Convert to datetime64[ns] since it is pandas default type for timestamps
42
+ df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
43
+
44
+ # Contract code format was changed in 22/05/2006
45
+ if trade_date < pd.Timestamp("2006-05-22"):
46
+ df["ExpirationDate"] = df["ExpirationCode"].apply(
47
+ common.get_old_expiration_date, args=(trade_date,)
48
+ )
49
+ else:
50
+ df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
51
+
52
+ # Replace 0 values in rate columns with pd.NA and remove percentage
53
+ rate_cols = [col for col in df.columns if "Rate" in col]
54
+ # Round to 5 decimal places (3 in %) since it is the contract's precision
55
+ df[rate_cols] = df[rate_cols].replace(0, pd.NA).div(100).round(5)
56
+
57
+ df["TickerSymbol"] = "FRC" + df["ExpirationCode"]
58
+
59
+ # Filter and order columns
60
+ ordered_cols = [
61
+ "TradeDate",
62
+ "TickerSymbol",
63
+ # "ExpirationCode",
64
+ "ExpirationDate",
65
+ "TradeCount",
66
+ "TradeVolume",
67
+ "FinancialVolume",
68
+ "SettlementRate",
69
+ "FirstRate",
70
+ "MinRate",
71
+ "AvgRate",
72
+ "MaxRate",
73
+ "CloseAskRate",
74
+ "CloseBidRate",
75
+ "CloseRate",
76
+ ]
77
+ return df[ordered_cols]
78
+
79
+
80
+ def fetch_past_frc(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
81
+ """
82
+ Fetchs the DI futures data for a given date from B3.
83
+
84
+ This function fetches and processes the DI futures data from B3 for a specific
85
+ trade date. It's the primary external interface for accessing DI data.
86
+
87
+ Args:
88
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
89
+ raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
90
+ Defaults to False.
91
+
92
+ Returns:
93
+ pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
94
+ """
95
+ df_raw = common.fetch_past_raw_df(asset_code="FRC", trade_date=trade_date)
96
+ if return_raw:
97
+ return df_raw
98
+ return _process_past_data(df_raw, trade_date)
@@ -41,7 +41,7 @@ def fetch_ipca_mr(reference_date: pd.Timestamp) -> float | None:
41
41
  # Extract and return the IPCA monthly growth rate if data is available
42
42
  if data:
43
43
  ipca_str = data[0]["resultados"][0]["series"][0]["serie"][ipca_date]
44
- return float(ipca_str)
44
+ return round(float(ipca_str) / 100, 4)
45
45
  else:
46
46
  return None
47
47
 
@@ -52,8 +52,62 @@ def fetch_selic_target(reference_date: pd.Timestamp) -> float | None:
52
52
  api_url = f"https://api.bcb.gov.br/dados/serie/bcdata.sgs.432/dados?formato=json&dataInicial={selic_date}&dataFinal={selic_date}"
53
53
  response = requests.get(api_url)
54
54
  response.raise_for_status()
55
- data = response.json()
56
- if data:
57
- return float(data[0]["valor"])
55
+
56
+ if selic_date in response.text:
57
+ data = response.json()
58
+ return round(float(data[0]["valor"]) / 100, 4)
58
59
  else:
59
60
  return None
61
+
62
+
63
+ def fetch_di(reference_date: pd.Timestamp) -> float | None:
64
+ # https://api.bcb.gov.br/dados/serie/bcdata.sgs.11/dados?formato=json&dataInicial=12/04/2024&dataFinal=12/04/2024
65
+ di_date = reference_date.strftime("%d/%m/%Y")
66
+ api_url = f"https://api.bcb.gov.br/dados/serie/bcdata.sgs.11/dados?formato=json&dataInicial={di_date}&dataFinal={di_date}"
67
+ response = requests.get(api_url)
68
+ response.raise_for_status()
69
+
70
+ if di_date in response.text:
71
+ data = response.json()
72
+ return round(float(data[0]["valor"]) / 100, 8)
73
+ else:
74
+ return None
75
+
76
+
77
+ def fetch_vna_selic(reference_date: pd.Timestamp) -> float | None:
78
+ # url example: https://www3.bcb.gov.br/novoselic/rest/arquivosDiarios/pub/download/3/20240418APC238
79
+
80
+ url_base = "https://www3.bcb.gov.br/novoselic/rest/arquivosDiarios/pub/download/3/"
81
+ url_file = f"{reference_date.strftime("%Y%m%d")}APC238"
82
+ url_vna = url_base + url_file
83
+ response = requests.get(url_vna)
84
+ response.raise_for_status()
85
+ file_text = response.text
86
+
87
+ # Finding the part that contains the table
88
+ start_of_table = file_text.find("EMISSAO")
89
+ end_of_table = file_text.find("99999999*")
90
+
91
+ # Extracting the table
92
+ table_text = file_text[start_of_table:end_of_table].strip()
93
+ table_lines = table_text.splitlines()
94
+
95
+ # Remove empty lines
96
+ table_lines = [line.strip() for line in table_lines if line.strip()]
97
+
98
+ # Remove first line
99
+ body_lines = table_lines[1:]
100
+
101
+ vnas = []
102
+ for line in body_lines:
103
+ vna_str = line.split()[-1].replace(",", ".")
104
+ vna_float = float(vna_str)
105
+ vnas.append(vna_float)
106
+
107
+ # Raise error if all values are not the same
108
+ vna_value = vnas[0]
109
+ if any(vna_value != vna for vna in vnas):
110
+ bcb_url = "https://www.bcb.gov.br/estabilidadefinanceira/selicbaixar"
111
+ raise ValueError(f"VNA values are not the same. Please check data at {bcb_url}")
112
+
113
+ return vna_value
@@ -0,0 +1,87 @@
1
+ import io
2
+ import locale
3
+ from dataclasses import dataclass
4
+
5
+ import pandas as pd
6
+ import python_calamine as pc
7
+ import requests
8
+
9
+
10
+ @dataclass
11
+ class IndicatorProjection:
12
+ last_updated: pd.Timestamp # Date and time of the last update
13
+ reference_month_ts: pd.Timestamp # Timestamp to which the projection applies
14
+ reference_month_br: str # Brazilian formatted month (e.g., "ABR/2024")
15
+ projected_value: float # Projected value
16
+
17
+
18
+ def fetch_current_month_ipca_projection() -> IndicatorProjection:
19
+ """
20
+ Fetches the current month's IPCA projection from the ANBIMA website and returns it
21
+ as an IndicatorProjection instance.
22
+
23
+ This function retrieves and parses the Excel file that contains economic indicators,
24
+ specifically looking for the IPCA projection. It extracts the date of the last
25
+ update and the IPCA projection for the reference month.
26
+
27
+ Args:
28
+ None
29
+
30
+ Returns:
31
+ IndicatorProjection: An instance of IndicatorProjection containing:
32
+ - last_updated (pd.Timestamp): The datetime when the data was last updated.
33
+ - reference_month_ts (pd.Timestamp): The month to which the IPCA projection
34
+ applies.
35
+ - reference_month_br (str): The formatted month as a string
36
+ (e.g., "ABR/2024") using the pt_BR locale.
37
+ - projected_value (float): The projected IPCA value.
38
+
39
+ Example:
40
+ >>> projection = fetch_current_month_ipca_projection()
41
+ >>> print(projection)
42
+ IndicatorProjection(
43
+ last_updated=pd.Timestamp('2024-04-19 18:55:00'),
44
+ reference_month_ts=pd.Timestamp('2024-04-01 00:00:00'),
45
+ reference_month_br='ABR/2024',
46
+ projected_value=0.35
47
+ )
48
+
49
+ Data file format example after parsing:
50
+ - ['Data e Hora da Última Atualização: 19/04/2024 - 18:55 h', '', '']
51
+ - ...
52
+ - ['IPCA1', 'Projeção (abr/24)', 0.35]
53
+ - ...
54
+ """
55
+ # Define the URL and get the data
56
+ url = "https://www.anbima.com.br/informacoes/indicadores/arqs/indicadores.xls"
57
+ response = requests.get(url)
58
+ excel_data = io.BytesIO(response.content)
59
+
60
+ # Load the workbook and select the first sheet
61
+ workbook = pc.load_workbook(excel_data)
62
+ first_sheet = workbook.sheet_names[0]
63
+ data = workbook.get_sheet_by_name(first_sheet).to_python(skip_empty_area=True)
64
+
65
+ # Extract projection update date and time from the first row
66
+ last_update_str = data[0][0].split("Atualização:")[-1].strip()
67
+ last_updated = pd.to_datetime(last_update_str, format="%d/%m/%Y - %H:%M h")
68
+
69
+ # Find the row containing the IPCA projection and extract its data
70
+ ipca_data = next(line for line in data if "IPCA1" in line)
71
+
72
+ # Convert the last element of the IPCA data row to float for the projection value
73
+ ipca_value = round(float(ipca_data[-1]) / 100, 4)
74
+
75
+ # Extract and format the reference month
76
+ month_text = ipca_data[1].split("(")[-1].split(")")[0]
77
+ locale.setlocale(locale.LC_TIME, "pt_BR.UTF-8")
78
+ ipca_month_ts = pd.to_datetime(month_text, format="%b/%y")
79
+ ipca_month_br = ipca_month_ts.strftime("%b/%Y").upper()
80
+ locale.setlocale(locale.LC_TIME, "") # Reset locale to default
81
+
82
+ return IndicatorProjection(
83
+ last_updated=last_updated,
84
+ reference_month_ts=ipca_month_ts,
85
+ reference_month_br=ipca_month_br,
86
+ projected_value=ipca_value,
87
+ )
@@ -150,7 +150,7 @@ def calculate_di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
150
150
  bond type and maturity date.
151
151
  """
152
152
  # Fetch DI rates and adjust the maturity date format for compatibility
153
- df_di = ft.fetch_past_di_data(reference_date)[["ExpirationDate", "SettlementRate"]]
153
+ df_di = ft.fetch_past_di(reference_date)[["ExpirationDate", "SettlementRate"]]
154
154
 
155
155
  # Renaming the columns to match the ANBIMA structure
156
156
  df_di.rename(columns={"ExpirationDate": "MaturityDate"}, inplace=True)
@@ -3,7 +3,7 @@ import pandas as pd
3
3
  from . import bday
4
4
 
5
5
 
6
- def _normalize_date(reference_date: str | pd.Timestamp | None = None) -> pd.Timestamp:
6
+ def _normalize_date(input_date: str | pd.Timestamp | None = None) -> pd.Timestamp:
7
7
  """
8
8
  Normalizes the given date to ensure it is a past business day at midnight. If no
9
9
  date is provided, it defaults to the last business day.
@@ -31,25 +31,25 @@ def _normalize_date(reference_date: str | pd.Timestamp | None = None) -> pd.Time
31
31
  >>> _normalize_date(pd.Timestamp('2023-04-01 15:30'))
32
32
  >>> _normalize_date()
33
33
  """
34
- if isinstance(reference_date, str):
34
+ if isinstance(input_date, str):
35
35
  # Convert string date to Timestamp and normalize to midnight
36
- normalized_date = pd.Timestamp(reference_date).normalize()
37
- elif isinstance(reference_date, pd.Timestamp):
36
+ normalized_date = pd.Timestamp(input_date).normalize()
37
+ elif isinstance(input_date, pd.Timestamp):
38
38
  # Normalize Timestamp to midnight
39
- normalized_date = reference_date.normalize()
40
- elif reference_date is None:
39
+ normalized_date = input_date.normalize()
40
+ elif input_date is None:
41
41
  # If no date is provided, use the last available business day
42
42
  today = pd.Timestamp.today().normalize()
43
43
  normalized_date = bday.offset_bdays(dates=today, offset=0, roll="backward")
44
44
  else:
45
- raise ValueError("Invalid date format.")
45
+ raise ValueError(f"Date format not recognized: {input_date}")
46
46
 
47
+ error_date = normalized_date.strftime("%d-%m-%Y")
47
48
  # Validate that the date is not in the future
48
49
  if normalized_date > pd.Timestamp.today().normalize():
49
- raise ValueError("Reference date cannot be in the future.")
50
-
50
+ raise ValueError(f"Date {error_date} is in the future")
51
51
  # Validate that the date is a business day
52
52
  if not bday.is_bday(normalized_date):
53
- raise ValueError("Reference date must be a business day.")
53
+ raise ValueError(f"Date {error_date} is not a business day")
54
54
 
55
55
  return normalized_date
@@ -29,9 +29,10 @@ dependencies = [
29
29
  "beautifulsoup4",
30
30
  "html5lib",
31
31
  "lxml",
32
+ "python-calamine>=0.2.0",
32
33
  ]
33
34
  dynamic = []
34
- version = "0.7.3"
35
+ version = "0.7.5"
35
36
 
36
37
  [project.license]
37
38
  file = "LICENSE"
@@ -1,5 +1,4 @@
1
1
  import pandas as pd
2
- import pytest
3
2
 
4
3
  from pyield import futures as ft
5
4
 
@@ -43,7 +42,7 @@ def test_settlement_rate_with_old_holiday_list():
43
42
 
44
43
  # 22-12-2023 is before the new holiday calendar
45
44
  test_date = pd.Timestamp("2023-12-22")
46
- df = ft.fetch_past_di_data(trade_date=test_date)
45
+ df = ft.fetch_past_di(trade_date=test_date)
47
46
  tickers = list(settlement_rates.keys()) # noqa: F841
48
47
  result = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
49
48
  assert result == list(settlement_rates.values())
@@ -68,7 +67,7 @@ def test_settlement_rates_with_current_holiday_list():
68
67
  "DI1F33": 0.10331,
69
68
  }
70
69
  test_date = pd.Timestamp("2023-12-26")
71
- df = ft.fetch_past_di_data(trade_date=test_date)
70
+ df = ft.fetch_past_di(trade_date=test_date)
72
71
  tickers = list(settlement_rates.keys()) # noqa: F841
73
72
  results = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
74
73
  assert results == list(settlement_rates.values())
@@ -76,5 +75,6 @@ def test_settlement_rates_with_current_holiday_list():
76
75
 
77
76
  def test_non_business_day():
78
77
  non_business_day = pd.Timestamp("2023-12-24")
79
- with pytest.raises(ValueError):
80
- ft.fetch_past_di_data(trade_date=non_business_day)
78
+ # Test if it an empty DataFrame is returned
79
+ df = ft.fetch_past_di(trade_date=non_business_day)
80
+ assert df.empty
@@ -1 +0,0 @@
1
- __version__ = "0.7.3"
@@ -1,95 +0,0 @@
1
- import pandas as pd
2
-
3
- from . import futures as ft
4
- from . import indicators as it
5
- from . import treasuries as tr
6
- from .utils import _normalize_date
7
-
8
-
9
- def fetch_asset(
10
- asset_code: str,
11
- reference_date: str | pd.Timestamp | None = None,
12
- **kwargs,
13
- ) -> pd.DataFrame:
14
- """
15
- Fetches data for a specified asset based on type and reference date.
16
-
17
- Args:
18
- asset_code (str): The asset code identifying the type of financial asset.
19
- Supported options:
20
- - "TRB": Treasury bonds (indicative rates from ANBIMA).
21
- - "LTN", "LFT", "NTN-F", "NTN-B": Specific types of Brazilian treasury bonds
22
- (indicative rates from ANBIMA).
23
- - "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
24
- - "DDI": DI x U.S. Dollar Spread Futures (Futuro de Cupom Cambial) from B3.
25
- reference_date (str | pd.Timestamp | None): The reference date for which data is
26
- fetched. Defaults to the last business day if None.
27
- **kwargs: Additional keyword arguments, specifically:
28
- - return_raw (bool): Whether to return raw data without processing. Defaults
29
- to False.
30
-
31
- Returns:
32
- pd.DataFrame: A DataFrame containing the fetched data for the specified asset.
33
-
34
- Raises:
35
- ValueError: If the asset code is not recognized or supported.
36
-
37
- Examples:
38
- >>> fetch_asset('TRB', '2023-04-01')
39
- >>> fetch_asset('DI1', '2023-04-01', return_raw=True)
40
- """
41
- return_raw = kwargs.get("return_raw", False)
42
- normalized_date = _normalize_date(reference_date)
43
-
44
- if asset_code.lower() == "trb":
45
- return tr.fetch_bonds(reference_date=normalized_date, return_raw=return_raw)
46
- elif asset_code.lower() in ["ltn", "lft", "ntn-f", "ntn-b"]:
47
- df = tr.fetch_bonds(reference_date=normalized_date)
48
- return df.query(f"BondType == '{asset_code.upper()}'")
49
-
50
- elif asset_code.lower() == "di1":
51
- today = pd.Timestamp.today().normalize()
52
- if normalized_date == today:
53
- return ft.fetch_last_di_data()
54
- else:
55
- return ft.fetch_past_di_data(
56
- trade_date=normalized_date, return_raw=return_raw
57
- )
58
- elif asset_code.lower() == "ddi":
59
- return ft.fetch_ddi(trade_date=normalized_date, return_raw=return_raw)
60
- else:
61
- raise ValueError("Asset type not supported.")
62
-
63
-
64
- def fetch_indicator(
65
- indicator_code: str,
66
- reference_date: str | pd.Timestamp | None = None,
67
- ) -> float | None:
68
- """
69
- Fetches data for a specified economic indicator and reference date.
70
-
71
- Args:
72
- indicator_code (str): The code for the economic indicator. Supported options:
73
- - "SELIC": SELIC target rate from the Central Bank of Brazil.
74
- - "IPCA": IPCA monthly inflation rate from IBGE.
75
- reference_date (str | pd.Timestamp | None): The reference date for which data is
76
- fetched. Defaults to the last business day if None.
77
-
78
- Returns:
79
- pd.Series: A Series containing the fetched data for the specified indicator.
80
-
81
- Raises:
82
- ValueError: If the indicator code is not recognized or supported.
83
-
84
- Examples:
85
- >>> fetch_indicator('SELIC', '2023-04-01')
86
- >>> fetch_indicator('IPCA', '2023-04-01')
87
- """
88
- normalized_date = _normalize_date(reference_date)
89
-
90
- if indicator_code.lower() == "selic":
91
- return it.fetch_selic_target(reference_date=normalized_date)
92
- elif indicator_code.lower() == "ipca":
93
- return it.fetch_ipca_mr(reference_date=normalized_date)
94
- else:
95
- raise ValueError("Indicator type not supported.")
@@ -1,11 +0,0 @@
1
- from .common import get_expiration_date, get_old_expiration_date
2
- from .ddi import fetch_ddi
3
- from .di import fetch_last_di_data, fetch_past_di_data
4
-
5
- __all__ = [
6
- "fetch_past_di_data",
7
- "fetch_last_di_data",
8
- "fetch_ddi",
9
- "get_expiration_date",
10
- "get_old_expiration_date",
11
- ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes