PYield 0.7.4__tar.gz → 0.7.6__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 (28) hide show
  1. {pyield-0.7.4 → pyield-0.7.6}/PKG-INFO +52 -26
  2. {pyield-0.7.4 → pyield-0.7.6}/README.md +51 -25
  3. pyield-0.7.6/pyield/__about__.py +1 -0
  4. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/common.py +3 -2
  5. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/ddi.py +16 -49
  6. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/di.py +32 -51
  7. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/frc.py +9 -5
  8. {pyield-0.7.4 → pyield-0.7.6}/pyproject.toml +1 -1
  9. {pyield-0.7.4 → pyield-0.7.6}/tests/test_futures.py +3 -3
  10. pyield-0.7.4/pyield/__about__.py +0 -1
  11. {pyield-0.7.4 → pyield-0.7.6}/LICENSE +0 -0
  12. {pyield-0.7.4 → pyield-0.7.6}/pyield/__init__.py +0 -0
  13. {pyield-0.7.4 → pyield-0.7.6}/pyield/bday.py +0 -0
  14. {pyield-0.7.4 → pyield-0.7.6}/pyield/data_access.py +0 -0
  15. {pyield-0.7.4 → pyield-0.7.6}/pyield/data_analysis.py +0 -0
  16. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/__init__.py +0 -0
  17. {pyield-0.7.4 → pyield-0.7.6}/pyield/futures/di_xml.py +0 -0
  18. {pyield-0.7.4 → pyield-0.7.6}/pyield/holidays/__init__.py +0 -0
  19. {pyield-0.7.4 → pyield-0.7.6}/pyield/holidays/br_holidays_new.txt +0 -0
  20. {pyield-0.7.4 → pyield-0.7.6}/pyield/holidays/br_holidays_old.txt +0 -0
  21. {pyield-0.7.4 → pyield-0.7.6}/pyield/holidays/core.py +0 -0
  22. {pyield-0.7.4 → pyield-0.7.6}/pyield/indicators.py +0 -0
  23. {pyield-0.7.4 → pyield-0.7.6}/pyield/projections.py +0 -0
  24. {pyield-0.7.4 → pyield-0.7.6}/pyield/py.typed +0 -0
  25. {pyield-0.7.4 → pyield-0.7.6}/pyield/treasuries.py +0 -0
  26. {pyield-0.7.4 → pyield-0.7.6}/pyield/utils.py +0 -0
  27. {pyield-0.7.4 → pyield-0.7.6}/tests/__init__.py +0 -0
  28. {pyield-0.7.4 → pyield-0.7.6}/tests/test_bday.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.7.4
3
+ Version: 0.7.6
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>
@@ -45,7 +45,7 @@ Description-Content-Type: text/markdown
45
45
  [![Made with Python](https://img.shields.io/badge/Python->=3.11-blue?logo=python&logoColor=white)](https://python.org "Go to Python homepage")
46
46
  [![License](https://img.shields.io/badge/License-MIT-blue)](#license)
47
47
 
48
- # PYield: Fixed Income Toolbox for Brazilian Markets
48
+ # PYield: Brazilian Fixed Income Toolkit
49
49
 
50
50
  ## Introduction
51
51
 
@@ -99,17 +99,27 @@ dtype: datetime64[ns]
99
99
 
100
100
  ### Futures Data
101
101
  ```python
102
- # 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
103
114
  >>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
104
-
105
115
  TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastBidRate SettlementRate
106
- 2024-03-08 DI1J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
107
- 2024-03-08 DI1K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
108
- 2024-03-08 DI1M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
109
- ... ... ... ... ... ... ... ... ...
110
- 2024-03-08 DI1F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
111
- 2024-03-08 DI1F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
112
- 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
113
123
  ```
114
124
 
115
125
  ### Treasury Bonds Data
@@ -119,14 +129,14 @@ TradeDate TickerSymbol ExpirationDate BDaysToExp ... LastRate LastAskRate LastB
119
129
  # Obs: Anbima members have access to the full history
120
130
  >>> yd.fetch_asset(asset_code="NTN-B", reference_date='2024-04-12')
121
131
 
122
- BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
123
- NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
124
- NTN-B 2024-04-12 2025-05-15 0.05945 0.05913 0.05930 4,361.34391
125
- NTN-B 2024-04-12 2026-08-15 0.05927 0.05897 0.05910 4,301.40082
126
- ... ... ... ... ... ... ...
127
- NTN-B 2024-04-12 2050-08-15 0.06039 0.06006 0.06023 4,299.28233
128
- NTN-B 2024-04-12 2055-05-15 0.06035 0.05998 0.06017 4,367.13360
129
- 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
130
140
  ```
131
141
 
132
142
  ### Spreads Calculation
@@ -135,13 +145,13 @@ NTN-B 2024-04-12 2060-08-15 0.06057 0.06016 0.06036 4,292.26323
135
145
  >>> yd.calculate_spreads(spread_type="di_vs_pre", reference_date="2024-4-11")
136
146
 
137
147
  BondType ReferenceDate MaturityDate DISpread
138
- LTN 2024-04-11 2024-07-01 -20.28000
139
- LTN 2024-04-11 2024-10-01 -10.19000
140
- LTN 2024-04-11 2025-01-01 -15.05000
141
- ... ... ... ...
142
- NTN-F 2024-04-11 2031-01-01 -0.66000
143
- NTN-F 2024-04-11 2033-01-01 -5.69000
144
- 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
145
155
  ```
146
156
 
147
157
  ### Indicators Data
@@ -158,3 +168,19 @@ NTN-F 2024-04-11 2035-01-01 -1.27000
158
168
  >>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
159
169
  None
160
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.6"
@@ -133,7 +133,8 @@ def get_old_expiration_date(
133
133
 
134
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_raw_df(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),
@@ -15,29 +15,8 @@ def _convert_prices_to_rates(prices: pd.Series, n_days: pd.Series) -> pd.Series:
15
15
  pd.Series: A pd.Series containing DDI futures rates.
16
16
  """
17
17
  rates = (100_000 / prices - 1) * (360 / n_days)
18
-
19
- # Return rates as percentage
20
- return 100 * rates
21
-
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
18
+ # Round to 5 (3 in %) dec. places (contract's current max. precision)
19
+ return rates.round(5)
41
20
 
42
21
 
43
22
  def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
@@ -90,46 +69,34 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
90
69
  else:
91
70
  df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
92
71
 
93
- df["DaysToExpiration"] = (df["ExpirationDate"] - trade_date).dt.days
72
+ df["DaysToExp"] = (df["ExpirationDate"] - trade_date).dt.days
94
73
  # Convert to nullable integer, since other columns use this data type
95
- df["DaysToExpiration"] = df["DaysToExpiration"].astype(pd.Int64Dtype())
74
+ df["DaysToExp"] = df["DaysToExp"].astype(pd.Int64Dtype())
75
+
96
76
  # Remove expired contracts
97
- df.query("DaysToExpiration > 0", inplace=True)
77
+ df.query("DaysToExp > 0", inplace=True)
98
78
 
99
79
  # 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)
80
+ rate_cols = [col for col in df.columns if "Rate" in col]
81
+ cols_with_nan = rate_cols + ["SettlementPrice"]
82
+ # Replace 0 with NaN in these columns
83
+ df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
112
84
 
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)
85
+ df[rate_cols] = df[rate_cols].div(100).round(5)
116
86
 
87
+ # Calculate SettlementRate
117
88
  df["SettlementRate"] = _convert_prices_to_rates(
118
- df["SettlementPrice"], df["DaysToExpiration"]
89
+ df["SettlementPrice"], df["DaysToExp"]
119
90
  )
120
91
 
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
- 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)
92
+ df["TickerSymbol"] = "DI1" + df["ExpirationCode"]
126
93
 
127
94
  # Filter and order columns
128
95
  ordered_cols = [
129
96
  "TradeDate",
130
97
  "ExpirationCode",
131
98
  "ExpirationDate",
132
- "DaysToExpiration",
99
+ "DaysToExp",
133
100
  "OpenContracts",
134
101
  # "OpenContractsEndSession" since there is no OpenContracts at the end of the
135
102
  # day in XML data, it will be removed to avoid confusion with XML data
@@ -169,7 +136,7 @@ def fetch_past_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Dat
169
136
  >>> di.fetch_ddi(pd.Timestamp("2021-01-04"))
170
137
 
171
138
  Notes:
172
- - DaysToExpiration: number of business days to ExpirationDate.
139
+ - DaysToExp: number of business days to ExpirationDate.
173
140
  - OpenContracts: number of open contracts at the start of the trading day.
174
141
  """
175
142
  df_raw = common.fetch_past_raw_df(asset_code="DDI", trade_date=trade_date)
@@ -1,7 +1,7 @@
1
1
  import pandas as pd
2
2
 
3
- from .. import bday
4
- from . import common
3
+ from .. import bday as bd
4
+ from . import common as cm
5
5
 
6
6
 
7
7
  def _convert_prices_to_rates(prices: pd.Series, bd: pd.Series) -> pd.Series:
@@ -17,22 +17,12 @@ def _convert_prices_to_rates(prices: pd.Series, bd: pd.Series) -> pd.Series:
17
17
  """
18
18
  rates = (100_000 / prices) ** (252 / bd) - 1
19
19
 
20
- # Return rates as percentage
21
- return 100 * rates
20
+ # Round to 5 (3 in %) dec. places (contract's current max. precision)
21
+ return rates.round(5)
22
22
 
23
23
 
24
- def _convert_prices_in_older_contracts(df: pd.DataFrame) -> pd.DataFrame:
25
- # Prior to 01/01/2002, prices were not converted to rates
26
- convert_cols = [
27
- "FirstRate",
28
- "MinRate",
29
- "MaxRate",
30
- "AvgRate",
31
- "CloseRate",
32
- "CloseBidRate",
33
- "CloseAskRate",
34
- ]
35
- for col in convert_cols:
24
+ def _adjust_older_contracts_rates(df: pd.DataFrame, rate_cols: list) -> pd.DataFrame:
25
+ for col in rate_cols:
36
26
  df[col] = _convert_prices_to_rates(df[col], df["BDaysToExp"])
37
27
 
38
28
  # Invert low and high prices
@@ -52,10 +42,6 @@ def _process_past_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataF
52
42
  Returns:
53
43
  pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
54
44
  """
55
- # Check if the pd.DataFrame is empty
56
- if df.empty:
57
- return df
58
-
59
45
  rename_dict = {
60
46
  "VENCTO": "ExpirationCode",
61
47
  "CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
@@ -86,43 +72,34 @@ def _process_past_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataF
86
72
  # Contract code format was changed in 22/05/2006
87
73
  if trade_date < pd.Timestamp("2006-05-22"):
88
74
  df["ExpirationDate"] = df["ExpirationCode"].apply(
89
- common.get_old_expiration_date, args=(trade_date,)
75
+ cm.get_old_expiration_date, args=(trade_date,)
90
76
  )
91
77
  else:
92
- df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
78
+ df["ExpirationDate"] = df["ExpirationCode"].apply(cm.get_expiration_date)
79
+
80
+ df["BDaysToExp"] = bd.count_bdays(trade_date, df["ExpirationDate"])
93
81
 
94
- df["BDaysToExp"] = bday.count_bdays(trade_date, df["ExpirationDate"])
95
82
  # Remove expired contracts
96
83
  df.query("BDaysToExp > 0", inplace=True)
97
84
 
98
85
  # 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
- # Prior to 17/01/2002 (incluive), prices were not converted to rates
113
- if trade_date <= pd.Timestamp("2002-01-17"):
114
- df = _convert_prices_in_older_contracts(df)
86
+ rate_cols = [col for col in df.columns if "Rate" in col]
87
+ cols_with_nan = rate_cols + ["SettlementPrice"]
88
+ # Replace 0 with NaN in these columns
89
+ df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
90
+
91
+ # Prior to 17/01/2002 (inclusive), prices were not converted to rates
92
+ if trade_date > pd.Timestamp("2002-01-17"):
93
+ # Remove % and round to 5 (3 in %) dec. places in rate columns
94
+ df[rate_cols] = df[rate_cols].div(100).round(5)
95
+ else:
96
+ df = _adjust_older_contracts_rates(df, rate_cols)
115
97
 
98
+ # Calculate SettlementRate
116
99
  df["SettlementRate"] = _convert_prices_to_rates(
117
100
  df["SettlementPrice"], df["BDaysToExp"]
118
101
  )
119
102
 
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
- 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)
125
-
126
103
  df["TickerSymbol"] = "DI1" + df["ExpirationCode"]
127
104
 
128
105
  # Filter and order columns
@@ -178,7 +155,7 @@ def _process_last_raw_di_df(raw_df: pd.DataFrame) -> pd.DataFrame:
178
155
  # Rename columns
179
156
  df = df.rename(columns=rename_columns)
180
157
 
181
- df["BDaysToExp"] = bday.count_bdays(df["TradeTimestamp"], df["ExpirationDate"])
158
+ df["BDaysToExp"] = bd.count_bdays(df["TradeTimestamp"], df["ExpirationDate"])
182
159
 
183
160
  # Remove percentage in all rate columns
184
161
  rate_cols = [col for col in df.columns if "Rate" in col]
@@ -195,7 +172,7 @@ def fetch_last_di() -> pd.DataFrame:
195
172
  Returns:
196
173
  pd.DataFrame: A Pandas pd.DataFrame containing the latest DI futures data.
197
174
  """
198
- raw_df = common.fetch_last_raw_df(future_code="DI1")
175
+ raw_df = cm.fetch_last_raw_df(future_code="DI1")
199
176
  return _process_last_raw_di_df(raw_df)
200
177
 
201
178
 
@@ -204,11 +181,12 @@ def fetch_past_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Data
204
181
  Fetchs the DI futures data for a given date from B3.
205
182
 
206
183
  This function fetches and processes the DI futures data from B3 for a specific
207
- trade date. It's the primary external interface for accessing DI data.
184
+ trade date. It's the primary external interface for accessing DI data. If the data
185
+ is not available, an empty DataFrame is returned.
208
186
 
209
187
  Args:
210
188
  trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
211
- raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
189
+ return_raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
212
190
  Defaults to False.
213
191
 
214
192
  Returns:
@@ -218,7 +196,10 @@ def fetch_past_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Data
218
196
  - BDaysToExp: number of business days to ExpirationDate.
219
197
  - OpenContracts: number of open contracts at the start of the trading day.
220
198
  """
221
- df_raw = common.fetch_past_raw_df(asset_code="DI1", trade_date=trade_date)
199
+ df_raw = cm.fetch_past_raw_df(asset_code="DI1", trade_date=trade_date)
222
200
  if return_raw:
223
201
  return df_raw
224
- return _process_past_raw_df(df_raw, trade_date)
202
+ if df_raw.empty:
203
+ return pd.DataFrame()
204
+ else:
205
+ return _process_past_raw_df(df_raw, trade_date)
@@ -41,14 +41,18 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
41
41
  # Convert to datetime64[ns] since it is pandas default type for timestamps
42
42
  df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
43
43
 
44
- df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
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)
45
51
 
46
52
  # Replace 0 values in rate columns with pd.NA and remove percentage
47
53
  rate_cols = [col for col in df.columns if "Rate" in col]
48
- for col in rate_cols:
49
- df[col] = df[col].replace(0, pd.NA)
50
- # Round to 5 decimal places (3 in %) since it is the contract's precision
51
- df[col] = (df[col] / 100).round(5)
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)
52
56
 
53
57
  df["TickerSymbol"] = "FRC" + df["ExpirationCode"]
54
58
 
@@ -32,7 +32,7 @@ dependencies = [
32
32
  "python-calamine>=0.2.0",
33
33
  ]
34
34
  dynamic = []
35
- version = "0.7.4"
35
+ version = "0.7.6"
36
36
 
37
37
  [project.license]
38
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
 
@@ -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(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.4"
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