PYield 0.7.0__tar.gz → 0.7.2__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.0 → pyield-0.7.2}/PKG-INFO +10 -10
  2. {pyield-0.7.0 → pyield-0.7.2}/README.md +9 -9
  3. pyield-0.7.2/pyield/__about__.py +1 -0
  4. {pyield-0.7.0 → pyield-0.7.2}/pyield/data_access.py +11 -10
  5. {pyield-0.7.0 → pyield-0.7.2}/pyield/data_analysis.py +3 -1
  6. pyield-0.7.2/pyield/futures/__init__.py +10 -0
  7. pyield-0.7.2/pyield/futures/common.py +175 -0
  8. pyield-0.7.2/pyield/futures/ddi.py +178 -0
  9. pyield-0.7.0/pyield/di/web.py → pyield-0.7.2/pyield/futures/di.py +16 -130
  10. pyield-0.7.0/pyield/di/xml.py → pyield-0.7.2/pyield/futures/di_xml.py +47 -7
  11. {pyield-0.7.0 → pyield-0.7.2}/pyield/treasuries.py +4 -4
  12. {pyield-0.7.0 → pyield-0.7.2}/pyproject.toml +1 -1
  13. pyield-0.7.0/tests/test_di.py → pyield-0.7.2/tests/test_futures.py +11 -11
  14. pyield-0.7.0/pyield/__about__.py +0 -1
  15. pyield-0.7.0/pyield/di/__init__.py +0 -6
  16. pyield-0.7.0/pyield/di/core.py +0 -180
  17. {pyield-0.7.0 → pyield-0.7.2}/LICENSE +0 -0
  18. {pyield-0.7.0 → pyield-0.7.2}/pyield/__init__.py +0 -0
  19. {pyield-0.7.0 → pyield-0.7.2}/pyield/bday.py +0 -0
  20. {pyield-0.7.0 → pyield-0.7.2}/pyield/holidays/__init__.py +0 -0
  21. {pyield-0.7.0 → pyield-0.7.2}/pyield/holidays/br_holidays_new.txt +0 -0
  22. {pyield-0.7.0 → pyield-0.7.2}/pyield/holidays/br_holidays_old.txt +0 -0
  23. {pyield-0.7.0 → pyield-0.7.2}/pyield/holidays/core.py +0 -0
  24. {pyield-0.7.0 → pyield-0.7.2}/pyield/indicators.py +0 -0
  25. {pyield-0.7.0 → pyield-0.7.2}/pyield/py.typed +0 -0
  26. {pyield-0.7.0 → pyield-0.7.2}/pyield/utils.py +0 -0
  27. {pyield-0.7.0 → pyield-0.7.2}/tests/__init__.py +0 -0
  28. pyield-0.7.0/tests/test_br_calendar.py → pyield-0.7.2/tests/test_bday.py +1 -1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: PYield
3
- Version: 0.7.0
3
+ Version: 0.7.2
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>
@@ -48,7 +48,7 @@ Description-Content-Type: text/markdown
48
48
 
49
49
  ## Introduction
50
50
 
51
- Welcome to PYield, a Python library designed for the analysis of fixed income instruments in Brazil. This library is tailored for financial analysts, researchers, and enthusiasts interested in the Brazilian fixed income market. Leveraging the power of popular Python libraries like Pandas and Requests, PYield simplifies the process of obtaining and processing data from key sources such as ANBIMA and B3.
51
+ Welcome to PYield, a Python library designed for the analysis of fixed income instruments in Brazil. This library is tailored for financial analysts, researchers, and enthusiasts interested in the Brazilian fixed income market. Leveraging the power of popular Python libraries like Pandas and Requests, PYield simplifies the process of obtaining and processing data from key sources such as ANBIMA, BCB, IBGE and B3.
52
52
 
53
53
  ## Features
54
54
 
@@ -101,14 +101,14 @@ dtype: datetime64[ns]
101
101
  # Fetch a DataFrame with the DI Futures data from B3
102
102
  >>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
103
103
 
104
- TradeDate ExpirationCode ExpirationDate BDToExpiration ... LastRate LastAskRate LastBidRate SettlementRate
105
- 2024-03-08 J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
106
- 2024-03-08 K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
107
- 2024-03-08 M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
108
- ... ... ... ... ... ... ... ... ...
109
- 2024-03-08 F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
110
- 2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
111
- 2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
104
+ TradeDate ExpirationCode ExpirationDate BDaysToExpiration ... LastRate LastAskRate LastBidRate SettlementRate
105
+ 2024-03-08 J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
106
+ 2024-03-08 K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
107
+ 2024-03-08 M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
108
+ ... ... ... ... ... ... ... ... ...
109
+ 2024-03-08 F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
110
+ 2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
111
+ 2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
112
112
  ```
113
113
 
114
114
  ### Treasury Bonds Data
@@ -6,7 +6,7 @@
6
6
 
7
7
  ## Introduction
8
8
 
9
- Welcome to PYield, a Python library designed for the analysis of fixed income instruments in Brazil. This library is tailored for financial analysts, researchers, and enthusiasts interested in the Brazilian fixed income market. Leveraging the power of popular Python libraries like Pandas and Requests, PYield simplifies the process of obtaining and processing data from key sources such as ANBIMA and B3.
9
+ Welcome to PYield, a Python library designed for the analysis of fixed income instruments in Brazil. This library is tailored for financial analysts, researchers, and enthusiasts interested in the Brazilian fixed income market. Leveraging the power of popular Python libraries like Pandas and Requests, PYield simplifies the process of obtaining and processing data from key sources such as ANBIMA, BCB, IBGE and B3.
10
10
 
11
11
  ## Features
12
12
 
@@ -59,14 +59,14 @@ dtype: datetime64[ns]
59
59
  # Fetch a DataFrame with the DI Futures data from B3
60
60
  >>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
61
61
 
62
- TradeDate ExpirationCode ExpirationDate BDToExpiration ... LastRate LastAskRate LastBidRate SettlementRate
63
- 2024-03-08 J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
64
- 2024-03-08 K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
65
- 2024-03-08 M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
66
- ... ... ... ... ... ... ... ... ...
67
- 2024-03-08 F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
68
- 2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
69
- 2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
62
+ TradeDate ExpirationCode ExpirationDate BDaysToExpiration ... LastRate LastAskRate LastBidRate SettlementRate
63
+ 2024-03-08 J24 2024-04-01 15 ... 10.952 10.952 10.956 10.956
64
+ 2024-03-08 K24 2024-05-02 37 ... 10.776 10.774 10.780 10.777
65
+ 2024-03-08 M24 2024-06-03 58 ... 10.604 10.602 10.604 10.608
66
+ ... ... ... ... ... ... ... ... ...
67
+ 2024-03-08 F37 2037-01-02 3213 ... <NA> <NA> <NA> 10.859
68
+ 2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
69
+ 2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
70
70
  ```
71
71
 
72
72
  ### Treasury Bonds Data
@@ -0,0 +1 @@
1
+ __version__ = "0.7.2"
@@ -1,7 +1,7 @@
1
1
  import pandas as pd
2
2
 
3
- from . import di
4
- from . import indicators as ir
3
+ from . import futures as ft
4
+ from . import indicators as it
5
5
  from . import treasuries as tr
6
6
  from .utils import _normalize_date
7
7
 
@@ -20,7 +20,8 @@ def fetch_asset(
20
20
  - "TRB": Treasury bonds (indicative rates from ANBIMA).
21
21
  - "LTN", "LFT", "NTN-F", "NTN-B": Specific types of Brazilian treasury bonds
22
22
  (indicative rates from ANBIMA).
23
- - "DI1": DI Futures rates from B3.
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.
24
25
  reference_date (str | pd.Timestamp | None): The reference date for which data is
25
26
  fetched. Defaults to the previous business day if None.
26
27
  **kwargs: Additional keyword arguments, specifically:
@@ -41,15 +42,15 @@ def fetch_asset(
41
42
  normalized_date = _normalize_date(reference_date)
42
43
 
43
44
  if asset_code.lower() == "trb":
44
- return tr.fetch_data(reference_date=normalized_date, return_raw=return_raw)
45
+ return tr.fetch_bonds(reference_date=normalized_date, return_raw=return_raw)
45
46
  elif asset_code.lower() in ["ltn", "lft", "ntn-f", "ntn-b"]:
46
- df = tr.fetch_data(reference_date=normalized_date)
47
+ df = tr.fetch_bonds(reference_date=normalized_date)
47
48
  return df.query(f"BondType == '{asset_code.upper()}'")
48
49
 
49
50
  elif asset_code.lower() == "di1":
50
- return di.fetch_data(
51
- trade_date=normalized_date, source_type="bmf", return_raw=return_raw
52
- )
51
+ return ft.fetch_di(trade_date=normalized_date, return_raw=return_raw)
52
+ elif asset_code.lower() == "ddi":
53
+ return ft.fetch_ddi(trade_date=normalized_date, return_raw=return_raw)
53
54
  else:
54
55
  raise ValueError("Asset type not supported.")
55
56
 
@@ -81,8 +82,8 @@ def fetch_indicator(
81
82
  normalized_date = _normalize_date(reference_date)
82
83
 
83
84
  if indicator_code.lower() == "selic":
84
- return ir.fetch_selic_target(reference_date=normalized_date)
85
+ return it.fetch_selic_target(reference_date=normalized_date)
85
86
  elif indicator_code.lower() == "ipca":
86
- return ir.fetch_ipca_mr(reference_date=normalized_date)
87
+ return it.fetch_ipca_mr(reference_date=normalized_date)
87
88
  else:
88
89
  raise ValueError("Indicator type not supported.")
@@ -4,7 +4,9 @@ from . import treasuries as tr
4
4
  from .utils import _normalize_date
5
5
 
6
6
 
7
- def calculate_spreads(spread_type: str, reference_date: pd.Timestamp) -> pd.DataFrame:
7
+ def calculate_spreads(
8
+ spread_type: str, reference_date: str | pd.Timestamp | None = None
9
+ ) -> pd.DataFrame:
8
10
  """
9
11
  Calculates spreads between assets based on the specified spread type.
10
12
  If no reference date is provided, the function uses the previous business day.
@@ -0,0 +1,10 @@
1
+ from .common import get_expiration_date, get_old_expiration_date
2
+ from .ddi import fetch_ddi
3
+ from .di import fetch_di
4
+
5
+ __all__ = [
6
+ "fetch_di",
7
+ "fetch_ddi",
8
+ "get_expiration_date",
9
+ "get_old_expiration_date",
10
+ ]
@@ -0,0 +1,175 @@
1
+ import io
2
+
3
+ import pandas as pd
4
+ import requests
5
+
6
+ from .. import bday
7
+
8
+
9
+ def get_expiration_date(expiration_code: str) -> pd.Timestamp:
10
+ """
11
+ Converts an expiration code into its corresponding expiration date.
12
+
13
+ This function translates an expiration code into a specific expiration date based on
14
+ a given mapping. The expiration code consists of a letter representing the month and
15
+ two digits for the year. The function ensures the date returned is a valid business
16
+ day by adjusting weekends and holidays as necessary.
17
+
18
+ Args:
19
+ expiration_code (str): The expiration code to be converted, where the first
20
+ letter represents the month and the last two digits represent the year
21
+ (e.g., "F23" for January 2023).
22
+
23
+ Returns:
24
+ pd.Timestamp: The expiration date corresponding to the code, adjusted to a valid
25
+ business day. Returns pd.NaT if the code is invalid.
26
+
27
+ Examples:
28
+ >>> get_expiration_date("F23")
29
+ pd.Timestamp('2023-01-01')
30
+
31
+ >>> get_expiration_date("Z33")
32
+ pd.Timestamp('2033-12-01')
33
+
34
+ >>> get_expiration_date("A99")
35
+ pd.NaT
36
+
37
+ Notes:
38
+ The expiration date is calculated based on the format change introduced by B3 on
39
+ 22-05-2006, where the first letter represents the month and the last two digits
40
+ represent the year.
41
+ """
42
+ month_codes = {
43
+ "F": 1,
44
+ "G": 2,
45
+ "H": 3,
46
+ "J": 4,
47
+ "K": 5,
48
+ "M": 6,
49
+ "N": 7,
50
+ "Q": 8,
51
+ "U": 9,
52
+ "V": 10,
53
+ "X": 11,
54
+ "Z": 12,
55
+ }
56
+
57
+ try:
58
+ month_code = expiration_code[0]
59
+ month = month_codes[month_code]
60
+ year = int("20" + expiration_code[-2:])
61
+ # The expiration date is always the first business day of the month
62
+ expiration = pd.Timestamp(year, month, 1)
63
+
64
+ # Adjust to the next business day when expiration date is a weekend or a holiday
65
+ adj_expiration = bday.offset_bdays(expiration, offset=0)
66
+
67
+ return adj_expiration
68
+
69
+ except (KeyError, ValueError):
70
+ return pd.NaT # type: ignore
71
+
72
+
73
+ def get_old_expiration_date(
74
+ expiration_code: str, trade_date: pd.Timestamp
75
+ ) -> pd.Timestamp:
76
+ """
77
+ Internal function to convert an old DI contract code into its ExpirationDate date.
78
+ Valid for contract codes up to 21-05-2006.
79
+
80
+ Args:
81
+ expiration_code (str): An old DI Expiration Code from B3, where the first three
82
+ letters represent the month and the last digit represents the year.
83
+ Example: "JAN3".
84
+ trade_date (pd.Timestamp): The trade date for which the contract code is valid.
85
+
86
+ Returns:
87
+ pd.Timestamp
88
+ The contract's ExpirationDate date. Returns pd.NaT if the input is invalid.
89
+
90
+ Examples:
91
+ >>> get_old_expiration_date("JAN3", pd.Timestamp("2001-05-21"))
92
+ pd.Timestamp('2003-01-01')
93
+
94
+ Notes:
95
+ - In 22-05-2006, B3 changed the format of the DI contract codes. Before that
96
+ date, the first three letters represented the month and the last digit
97
+ represented the year.
98
+ """
99
+
100
+ month_codes = {
101
+ "JAN": 1,
102
+ "FEV": 2,
103
+ "MAR": 3,
104
+ "ABR": 4,
105
+ "MAI": 5,
106
+ "JUN": 6,
107
+ "JUL": 7,
108
+ "AGO": 8,
109
+ "SET": 9,
110
+ "OUT": 10,
111
+ "NOV": 11,
112
+ "DEZ": 12,
113
+ }
114
+ try:
115
+ month_code = expiration_code[:3]
116
+ month = month_codes[month_code]
117
+
118
+ # Year codes must generated dynamically, since it depends on the trade date.
119
+ reference_year = trade_date.year
120
+ year_codes = {}
121
+ for year in range(reference_year, reference_year + 10):
122
+ year_codes[str(year)[-1:]] = year
123
+ year = year_codes[expiration_code[-1:]]
124
+
125
+ expiration_date = pd.Timestamp(year, month, 1)
126
+ # Adjust to the next business day when the date is a weekend or a holiday.
127
+ # Must use old holiday list, since this contract code was used until 2006.
128
+ return bday.offset_bdays(expiration_date, offset=0, holiday_list="old")
129
+
130
+ except (KeyError, ValueError):
131
+ return pd.NaT # type: ignore
132
+
133
+
134
+ def _fetch_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
135
+ """
136
+ Internal function to fetch raw DI futures data from B3 for a specific trade date.
137
+
138
+ Args:
139
+ trade_date: a datetime-like object representing the trade date.
140
+
141
+ Returns:
142
+ pd.DataFrame: Raw DI data as a Pandas pd.DataFrame.
143
+ """
144
+ url_date = trade_date.strftime("%d/%m/%Y")
145
+ # url example: https://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao_excel1.asp?Data=05/10/2023&Mercadoria=DI1
146
+ url = f"https://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao_excel1.asp?Data={url_date}&Mercadoria={asset_code}&XLS=false"
147
+ r = requests.get(url)
148
+
149
+ text = r.text
150
+ if "AJUSTE" not in text:
151
+ raise ValueError(f"Could not fetch data for {url_date}.")
152
+
153
+ df = pd.read_html(
154
+ io.StringIO(text),
155
+ match="AJUSTE",
156
+ header=1,
157
+ thousands=".",
158
+ decimal=",",
159
+ na_values=["-"],
160
+ dtype_backend="numpy_nullable",
161
+ )[0]
162
+
163
+ # Remove rows with all NaN values
164
+ df = df.dropna(how="all")
165
+
166
+ # Remove columns with all NaN values
167
+ df = df.dropna(axis=1, how="all")
168
+
169
+ # Force "VAR. PTOS." to be string, since it can also be read as float
170
+ df["VAR. PTOS."] = df["VAR. PTOS."].astype(pd.StringDtype())
171
+
172
+ # 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
+
175
+ return df
@@ -0,0 +1,178 @@
1
+ import pandas as pd
2
+
3
+ from . import common
4
+
5
+
6
+ def _convert_prices_to_rates(prices: pd.Series, n_days: pd.Series) -> pd.Series:
7
+ """
8
+ Internal function to convert DDI futures prices to rates.
9
+
10
+ Args:
11
+ prices (pd.Series): A pd.Series containing DDI futures prices.
12
+ bd (pd.Series): A serie containing the number of days to expiration.
13
+
14
+ Returns:
15
+ pd.Series: A pd.Series containing DDI futures rates.
16
+ """
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
41
+
42
+
43
+ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
44
+ """
45
+ Internal function to process and transform raw DI futures data.
46
+
47
+ Args:
48
+ df (pd.DataFrame): the raw DI DataFrame.
49
+ trade_date: a datetime-like object representing the trade date.
50
+
51
+ Returns:
52
+ pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
53
+ """
54
+ # Check if the pd.DataFrame is empty
55
+ if df.empty:
56
+ return df
57
+
58
+ rename_dict = {
59
+ "VENCTO": "ExpirationCode",
60
+ "CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
61
+ "CONTR. FECH.(2)": "OpenContractsEndSession", # At the end of the day
62
+ "NÚM. NEGOC.": "NumOfTrades",
63
+ "CONTR. NEGOC.": "TradedQuantity",
64
+ "VOL.": "FinancialVolume",
65
+ "AJUSTE": "SettlementPrice",
66
+ "AJUSTE ANTER. (3)": "PrevSettlementRate",
67
+ "AJUSTE CORRIG. (4)": "AdjSettlementRate",
68
+ "PREÇO MÍN.": "MinRate",
69
+ "PREÇO MÉD.": "AvgRate",
70
+ "PREÇO MÁX.": "MaxRate",
71
+ "PREÇO ABERTU.": "FirstRate",
72
+ "ÚLT. PREÇO": "LastRate",
73
+ "VAR. PTOS.": "PointsVariation",
74
+ # Attention: bid/ask rates are inverted
75
+ "ÚLT.OF. COMPRA": "LastAskRate",
76
+ "ÚLT.OF. VENDA": "LastBidRate",
77
+ }
78
+
79
+ df = df.rename(columns=rename_dict)
80
+
81
+ df["TradeDate"] = trade_date
82
+ # Convert to datetime64[ns] since it is pandas default type for timestamps
83
+ df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
84
+
85
+ # Contract code format was changed in 22/05/2006
86
+ if trade_date < pd.Timestamp("2006-05-22"):
87
+ df["ExpirationDate"] = df["ExpirationCode"].apply(
88
+ common.get_old_expiration_date, args=(trade_date,)
89
+ )
90
+ else:
91
+ df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
92
+
93
+ df["DaysToExpiration"] = (df["ExpirationDate"] - trade_date).dt.days
94
+ # Convert to nullable integer, since other columns use this data type
95
+ df["DaysToExpiration"] = df["DaysToExpiration"].astype(pd.Int64Dtype())
96
+ # Remove expired contracts
97
+ df.query("DaysToExpiration > 0", inplace=True)
98
+
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
+ df["SettlementRate"] = _convert_prices_to_rates(
118
+ df["SettlementPrice"], df["DaysToExpiration"]
119
+ )
120
+
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)
126
+
127
+ # Filter and order columns
128
+ ordered_cols = [
129
+ "TradeDate",
130
+ "ExpirationCode",
131
+ "ExpirationDate",
132
+ "DaysToExpiration",
133
+ "OpenContracts",
134
+ # "OpenContractsEndSession" since there is no OpenContracts at the end of the
135
+ # day in XML data, it will be removed to avoid confusion with XML data
136
+ "NumOfTrades",
137
+ "TradedQuantity",
138
+ "FinancialVolume",
139
+ "SettlementPrice",
140
+ "MinRate",
141
+ "AvgRate",
142
+ "MaxRate",
143
+ "FirstRate",
144
+ "LastRate",
145
+ "LastAskRate",
146
+ "LastBidRate",
147
+ "SettlementRate",
148
+ ]
149
+ return df[ordered_cols]
150
+
151
+
152
+ def fetch_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
153
+ """
154
+ Fetchs the DDI futures data for a given date from B3.
155
+
156
+ This function fetches and processes the DI futures data from B3 for a specific
157
+ trade date. It's the primary external interface for accessing DI data.
158
+
159
+ Args:
160
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
161
+ raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
162
+ Defaults to False.
163
+
164
+ Returns:
165
+ pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
166
+
167
+ Examples:
168
+ >>> from pyield.futures import di
169
+ >>> di.fetch_ddi(pd.Timestamp("2021-01-04"))
170
+
171
+ Notes:
172
+ - DaysToExpiration: number of business days to ExpirationDate.
173
+ - OpenContracts: number of open contracts at the start of the trading day.
174
+ """
175
+ df_raw = common._fetch_raw_df(asset_code="DDI", trade_date=trade_date)
176
+ if return_raw:
177
+ return df_raw
178
+ return _process_raw_df(df_raw, trade_date)
@@ -1,121 +1,7 @@
1
- import io
2
- import warnings
3
-
4
1
  import pandas as pd
5
- import requests
6
2
 
7
3
  from .. import bday
8
- from . import core as cr
9
-
10
-
11
- def get_old_expiration_date(
12
- expiration_code: str, trade_date: pd.Timestamp
13
- ) -> pd.Timestamp:
14
- """
15
- Internal function to convert an old DI contract code into its ExpirationDate date.
16
- Valid for contract codes up to 21-05-2006.
17
-
18
- Args:
19
- expiration_code (str): An old DI Expiration Code from B3, where the first three
20
- letters represent the month and the last digit represents the year.
21
- Example: "JAN3".
22
- trade_date (pd.Timestamp): The trade date for which the contract code is valid.
23
-
24
- Returns:
25
- pd.Timestamp
26
- The contract's ExpirationDate date. Returns pd.NaT if the input is invalid.
27
-
28
- Examples:
29
- >>> get_old_expiration_date("JAN3", pd.Timestamp("2001-05-21"))
30
- pd.Timestamp('2003-01-01')
31
-
32
- Notes:
33
- - In 22-05-2006, B3 changed the format of the DI contract codes. Before that
34
- date, the first three letters represented the month and the last digit
35
- represented the year.
36
- """
37
-
38
- month_codes = {
39
- "JAN": 1,
40
- "FEV": 2,
41
- "MAR": 3,
42
- "ABR": 4,
43
- "MAI": 5,
44
- "JUN": 6,
45
- "JUL": 7,
46
- "AGO": 8,
47
- "SET": 9,
48
- "OUT": 10,
49
- "NOV": 11,
50
- "DEZ": 12,
51
- }
52
- try:
53
- month_code = expiration_code[:3]
54
- month = month_codes[month_code]
55
-
56
- # Year codes must generated dynamically, since it depends on the trade date.
57
- reference_year = trade_date.year
58
- year_codes = {}
59
- for year in range(reference_year, reference_year + 10):
60
- year_codes[str(year)[-1:]] = year
61
- year = year_codes[expiration_code[-1:]]
62
-
63
- expiration_date = pd.Timestamp(year, month, 1)
64
- # Adjust to the next business day when the date is a weekend or a holiday.
65
- # Must use old holiday list, since this contract code was used until 2006.
66
- return bday.offset_bdays(expiration_date, offset=0, holiday_list="old")
67
-
68
- except (KeyError, ValueError):
69
- return pd.NaT # type: ignore
70
-
71
-
72
- def _get_raw_df(trade_date: pd.Timestamp) -> pd.DataFrame:
73
- """
74
- Internal function to fetch raw DI futures data from B3 for a specific trade date.
75
-
76
- Args:
77
- trade_date: a datetime-like object representing the trade date.
78
-
79
- Returns:
80
- pd.DataFrame: Raw DI data as a Pandas pd.DataFrame.
81
- """
82
- # url example: https://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao_excel1.asp?Data=05/10/2023&Mercadoria=DI1
83
- url = f"https://www2.bmf.com.br/pages/portal/bmfbovespa/boletim1/SistemaPregao_excel1.asp?Data={trade_date.strftime('%d/%m/%Y')}&Mercadoria=DI1&XLS=false"
84
- r = requests.get(url)
85
- f = io.StringIO(r.text)
86
-
87
- try:
88
- # Attempt to get the first table with the header "AJUSTE"
89
- df = pd.read_html(
90
- f,
91
- match="AJUSTE",
92
- header=1,
93
- thousands=".",
94
- decimal=",",
95
- na_values=["-"],
96
- dtype_backend="numpy_nullable",
97
- )[0]
98
-
99
- # Remove rows with all NaN values
100
- df = df.dropna(how="all")
101
-
102
- # Remove columns with all NaN values
103
- df = df.dropna(axis=1, how="all")
104
-
105
- # Force "VAR. PTOS." to be string, since it can also be read as float
106
- df["VAR. PTOS."] = df["VAR. PTOS."].astype(pd.StringDtype())
107
-
108
- # Force "AJUSTE CORRIG. (4)" to be float, since it can be also read as int
109
- df["AJUSTE CORRIG. (4)"] = df["AJUSTE CORRIG. (4)"].astype(pd.Float64Dtype())
110
-
111
- return df
112
-
113
- except Exception as e:
114
- trade_date_str = trade_date.strftime("%d/%m/%Y")
115
- message = f"""A {type(e).__name__} occurred while reading the DI futures data
116
- for {trade_date_str}. Returning an empty pd.DataFrame."""
117
- warnings.warn(message, stacklevel=2)
118
- return pd.DataFrame()
4
+ from . import common
119
5
 
120
6
 
121
7
  def _convert_prices_to_rates(prices: pd.Series, bd: pd.Series) -> pd.Series:
@@ -147,7 +33,7 @@ def _convert_prices_in_older_contracts(df: pd.DataFrame) -> pd.DataFrame:
147
33
  "LastAskRate",
148
34
  ]
149
35
  for col in convert_cols:
150
- df[col] = _convert_prices_to_rates(df[col], df["BDToExpiration"])
36
+ df[col] = _convert_prices_to_rates(df[col], df["BDaysToExpiration"])
151
37
 
152
38
  # Invert low and high prices
153
39
  df["MinRate"], df["MaxRate"] = df["MaxRate"], df["MinRate"]
@@ -200,16 +86,16 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
200
86
  # Contract code format was changed in 22/05/2006
201
87
  if trade_date < pd.Timestamp("2006-05-22"):
202
88
  df["ExpirationDate"] = df["ExpirationCode"].apply(
203
- get_old_expiration_date, args=(trade_date,)
89
+ common.get_old_expiration_date, args=(trade_date,)
204
90
  )
205
91
  else:
206
- df["ExpirationDate"] = df["ExpirationCode"].apply(cr.get_expiration_date)
92
+ df["ExpirationDate"] = df["ExpirationCode"].apply(common.get_expiration_date)
207
93
 
208
- df["BDToExpiration"] = bday.count_bdays(trade_date, df["ExpirationDate"])
94
+ df["BDaysToExpiration"] = bday.count_bdays(trade_date, df["ExpirationDate"])
209
95
  # Convert to nullable integer, since other columns use this data type
210
- df["BDToExpiration"] = df["BDToExpiration"].astype(pd.Int64Dtype())
96
+ df["BDaysToExpiration"] = df["BDaysToExpiration"].astype(pd.Int64Dtype())
211
97
  # Remove expired contracts
212
- df.query("BDToExpiration > 0", inplace=True)
98
+ df.query("BDaysToExpiration > 0", inplace=True)
213
99
 
214
100
  # Columns where 0 means NaN
215
101
  cols_with_nan = [
@@ -230,7 +116,7 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
230
116
  df = _convert_prices_in_older_contracts(df)
231
117
 
232
118
  df["SettlementRate"] = _convert_prices_to_rates(
233
- df["SettlementPrice"], df["BDToExpiration"]
119
+ df["SettlementPrice"], df["BDaysToExpiration"]
234
120
  )
235
121
 
236
122
  # Remove percentage in all rate columns and round to 5 decimal places since it's the
@@ -244,7 +130,7 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
244
130
  "TradeDate",
245
131
  "ExpirationCode",
246
132
  "ExpirationDate",
247
- "BDToExpiration",
133
+ "BDaysToExpiration",
248
134
  "OpenContracts",
249
135
  # "OpenContractsEndSession" since there is no OpenContracts at the end of the
250
136
  # day in XML data, it will be removed to avoid confusion with XML data
@@ -264,15 +150,15 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
264
150
  return df[ordered_cols]
265
151
 
266
152
 
267
- def get_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
153
+ def fetch_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
268
154
  """
269
- Gets the DI futures data for a given date from B3.
155
+ Fetchs the DI futures data for a given date from B3.
270
156
 
271
157
  This function fetches and processes the DI futures data from B3 for a specific
272
158
  trade date. It's the primary external interface for accessing DI data.
273
159
 
274
160
  Args:
275
- trade_date: a datetime-like object representing the trade date.
161
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
276
162
  raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
277
163
  Defaults to False.
278
164
 
@@ -280,14 +166,14 @@ def get_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
280
166
  pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
281
167
 
282
168
  Examples:
283
- >>> get_di("2023-12-28")
169
+ >>> from pyield.futures import di
170
+ >>> di.fetch_di(pd.Timestamp("2021-01-04"))
284
171
 
285
172
  Notes:
286
- - BDToExpiration: number of business days to ExpirationDate.
173
+ - BDaysToExpiration: number of business days to ExpirationDate.
287
174
  - OpenContracts: number of open contracts at the start of the trading day.
288
- - closed_contracts: number of closed contracts at the end of the trading day.
289
175
  """
290
- df_raw = _get_raw_df(trade_date)
176
+ df_raw = common._fetch_raw_df(asset_code="DI1", trade_date=trade_date)
291
177
  if return_raw:
292
178
  return df_raw
293
179
  return _process_raw_df(df_raw, trade_date)
@@ -8,7 +8,7 @@ from lxml import etree
8
8
  from pandas import DataFrame, Timestamp
9
9
 
10
10
  from .. import bday
11
- from . import core as cr
11
+ from . import common as cm
12
12
 
13
13
 
14
14
  def _get_file_from_url(trade_date: Timestamp, source_type: str) -> io.BytesIO:
@@ -97,12 +97,12 @@ def _extract_di_data_from_xml(xml_file: io.BytesIO) -> list[dict]:
97
97
  # Extrair a data de negociação
98
98
  if price_report is None:
99
99
  continue
100
- trade_date = price_report.find(".//ns:TradDt/ns:Dt", namespaces)
100
+ trade_date = price_report.find(".//ns:TradeDate/ns:Dt", namespaces)
101
101
 
102
102
  # Preparar o dicionário de dados do ticker com a data de negociação
103
103
  if trade_date is None:
104
104
  continue
105
- ticker_data = {"TradDt": trade_date.text, "TckrSymb": tckr_symb.text}
105
+ ticker_data = {"TradeDate": trade_date.text, "TckrSymb": tckr_symb.text}
106
106
 
107
107
  # Acessar o elemento FinInstrmAttrbts que contém o TckrSymb
108
108
  fin_instrm_attrbts = price_report.find(".//ns:FinInstrmAttrbts", namespaces)
@@ -229,17 +229,17 @@ def _process_di_df(df_raw: DataFrame) -> DataFrame:
229
229
  # Convert to datetime64[ns] since it is pandas default type for timestamps
230
230
  df["TradDt"] = df["TradDt"].astype("datetime64[ns]")
231
231
 
232
- expiration = df["TckrSymb"].str[3:].apply(cr.get_expiration_date)
232
+ expiration = df["TckrSymb"].str[3:].apply(cm.get_expiration_date)
233
233
  df.insert(2, "ExpirationDate", expiration)
234
234
 
235
235
  business_days = bday.count_bdays(df["TradDt"], df["ExpirationDate"])
236
- df.insert(3, "BDToExpiration", business_days)
236
+ df.insert(3, "BDaysToExpiration", business_days)
237
237
 
238
238
  # Convert to nullable integer, since other columns use this data type
239
- df["BDToExpiration"] = df["BDToExpiration"].astype(pd.Int64Dtype())
239
+ df["BDaysToExpiration"] = df["BDaysToExpiration"].astype(pd.Int64Dtype())
240
240
 
241
241
  # Remove expired contracts
242
- df.query("BDToExpiration > 0", inplace=True)
242
+ df.query("BDaysToExpiration > 0", inplace=True)
243
243
 
244
244
  return df.sort_values(by=["ExpirationDate"], ignore_index=True)
245
245
 
@@ -307,3 +307,43 @@ def read_di(file_path: Path, return_raw: bool = False) -> DataFrame:
307
307
 
308
308
  else:
309
309
  raise ValueError("A file path must be provided.")
310
+
311
+
312
+ def read_file(file_path: Path, return_raw: bool = False) -> pd.DataFrame:
313
+ """
314
+ Reads DI futures data from a file and returns it as a pandas DataFrame.
315
+
316
+ This function opens and reads a DI futures data file, returning the contents as a
317
+ pandas DataFrame. It supports reading from both XML files provided by B3, wich
318
+ are the simplified and complete Price Reports.
319
+
320
+ Args:
321
+ file_path (Path): The file path to the DI data file. This should be a valid
322
+ Path object pointing to the location of the file.
323
+ return_raw (bool, optional): If set to True, the function returns the raw data
324
+ without applying any transformation or processing. Useful for cases where
325
+ raw data inspection or custom processing is needed. Defaults to False.
326
+ source_type (Literal["bmf", "b3", "b3s"], optional): Indicates the source of
327
+ the data. Defaults to "bmf". Options include:
328
+ - "bmf": Fetches data from the old BM&FBOVESPA website. Fastest option.
329
+ - "b3": Fetches data from the complete Price Report (XML file) provided
330
+ by B3.
331
+ - "b3s": Fetches data from the simplified Price Report (XML file)
332
+ provided by B3. Faster than "b3" but less detailed.
333
+
334
+ Returns:
335
+ pd.DataFrame: A DataFrame containing the processed or raw DI futures data,
336
+ depending on the `return_raw` flag.
337
+
338
+ Examples:
339
+ >>> read_di(Path("path/to/di_data_file.xml"))
340
+ # returns a DataFrame with the DI futures data
341
+
342
+ >>> read_di(Path("path/to/di_data_file.xml"), return_raw=True)
343
+ # returns a DataFrame with the raw DI futures data, without processing
344
+
345
+ Note:
346
+ The ability to process and return raw data is primarily intended for advanced
347
+ users who require access to the data in its original form for custom analyses.
348
+ """
349
+ return read_di(file_path, return_raw=return_raw)
@@ -3,7 +3,7 @@ import io
3
3
  import pandas as pd
4
4
  import requests
5
5
 
6
- from . import di
6
+ from . import futures as ft
7
7
 
8
8
  # URL Constants
9
9
  ANBIMA_NON_MEMBER_URL = "https://www.anbima.com.br/informacoes/merc-sec/arqs/"
@@ -100,7 +100,7 @@ def _process_raw_df(df_raw: pd.DataFrame) -> pd.DataFrame:
100
100
  return df.sort_values(["BondType", "MaturityDate"], ignore_index=True)
101
101
 
102
102
 
103
- def fetch_data(reference_date: pd.Timestamp, return_raw=False) -> pd.DataFrame:
103
+ def fetch_bonds(reference_date: pd.Timestamp, return_raw=False) -> pd.DataFrame:
104
104
  """
105
105
  Fetches indicative treasury rates from ANBIMA for a specified reference date.
106
106
 
@@ -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 = di.fetch_data(reference_date)[["ExpirationDate", "SettlementRate"]]
153
+ df_di = ft.fetch_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)
@@ -159,7 +159,7 @@ def calculate_di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
159
159
  df_di["MaturityDate"] = df_di["MaturityDate"].dt.to_period("M").dt.to_timestamp()
160
160
 
161
161
  # Fetch bond rates, filtering for LTN and NTN-F types
162
- df_anbima = fetch_data(reference_date, False)
162
+ df_anbima = fetch_bonds(reference_date, False)
163
163
  df_anbima.query("BondType in ['LTN', 'NTN-F']", inplace=True)
164
164
 
165
165
  # Merge bond and DI rates by maturity date to calculate spreads
@@ -31,7 +31,7 @@ dependencies = [
31
31
  "lxml",
32
32
  ]
33
33
  dynamic = []
34
- version = "0.7.0"
34
+ version = "0.7.2"
35
35
 
36
36
  [project.license]
37
37
  file = "LICENSE"
@@ -1,14 +1,13 @@
1
1
  import pandas as pd
2
2
  import pytest
3
3
 
4
- from pyield.di import core as cr
5
- from pyield.di import web as web
4
+ from pyield import futures as ft
6
5
 
7
6
 
8
7
  def test_valid_old_contract_code1():
9
8
  expiration_code = "JAN3" # Valid contract code
10
9
  trade_date = pd.Timestamp("2001-05-21")
11
- result = web.get_old_expiration_date(expiration_code, trade_date)
10
+ result = ft.get_old_expiration_date(expiration_code, trade_date)
12
11
  contract_expiration = pd.Timestamp("2003-01-02")
13
12
  assert result == contract_expiration
14
13
 
@@ -16,7 +15,7 @@ def test_valid_old_contract_code1():
16
15
  def test_valid_old_contract_code2():
17
16
  expiration_code = "JAN3" # Valid contract code
18
17
  trade_date = pd.Timestamp("1990-01-01")
19
- result = web.get_old_expiration_date(expiration_code, trade_date)
18
+ result = ft.get_old_expiration_date(expiration_code, trade_date)
20
19
  contract_expiration = pd.Timestamp("1993-01-04")
21
20
  assert result == contract_expiration
22
21
 
@@ -25,13 +24,13 @@ def test_invalid_old_contract_code():
25
24
  expiration_code = "J3" # Invalid contract code
26
25
  trade_date = pd.Timestamp("2001-01-02")
27
26
  # Must return NaT
28
- result = web.get_old_expiration_date(expiration_code, trade_date)
27
+ result = ft.get_old_expiration_date(expiration_code, trade_date)
29
28
  assert pd.isnull(result)
30
29
 
31
30
 
32
31
  def test_new_contract_code():
33
32
  expiration_code = "F23" # Valid contract code
34
- result = cr.get_expiration_date(expiration_code)
33
+ result = ft.get_expiration_date(expiration_code)
35
34
  contract_expiration = pd.Timestamp("2023-01-02")
36
35
  assert result == contract_expiration
37
36
 
@@ -43,7 +42,8 @@ def test_settlement_rate_with_old_holiday_list():
43
42
  }
44
43
 
45
44
  # 22-12-2023 is before the new holiday calendar
46
- df = cr.fetch_data(trade_date="2023-12-22")
45
+ test_date = pd.Timestamp("2023-12-22")
46
+ df = ft.fetch_di(trade_date=test_date)
47
47
  expiration_codes = list(settlement_rates.keys()) # noqa: F841
48
48
  result = df.query("ExpirationCode in @expiration_codes")["SettlementRate"].to_list()
49
49
  assert result == list(settlement_rates.values())
@@ -67,8 +67,8 @@ def test_settlement_rates_with_current_holiday_list():
67
67
  "F31": 0.10240,
68
68
  "F33": 0.10331,
69
69
  }
70
-
71
- df = cr.fetch_data(trade_date="2023-12-26")
70
+ test_date = pd.Timestamp("2023-12-26")
71
+ df = ft.fetch_di(trade_date=test_date)
72
72
  expiration_codes = list(settlement_rates.keys()) # noqa: F841
73
73
  results = df.query("ExpirationCode in @expiration_codes")[
74
74
  "SettlementRate"
@@ -77,6 +77,6 @@ def test_settlement_rates_with_current_holiday_list():
77
77
 
78
78
 
79
79
  def test_non_business_day():
80
- non_business_day = "2023-12-24"
80
+ non_business_day = pd.Timestamp("2023-12-24")
81
81
  with pytest.raises(ValueError):
82
- cr.fetch_data(trade_date=non_business_day)
82
+ ft.fetch_di(trade_date=non_business_day)
@@ -1 +0,0 @@
1
- __version__ = "0.7.0"
@@ -1,6 +0,0 @@
1
- from .core import fetch_data, read_data
2
-
3
- __all__ = [
4
- "fetch_data",
5
- "read_data",
6
- ]
@@ -1,180 +0,0 @@
1
- from pathlib import Path
2
- from typing import Literal
3
-
4
- import pandas as pd
5
-
6
- from .. import bday
7
- from . import web, xml
8
-
9
-
10
- def _normalize_date(trade_date: str | pd.Timestamp | None = None) -> pd.Timestamp:
11
- if isinstance(trade_date, str):
12
- normalized_date = pd.Timestamp(trade_date).normalize()
13
- elif isinstance(trade_date, pd.Timestamp):
14
- normalized_date = trade_date.normalize()
15
- elif trade_date is None:
16
- today = pd.Timestamp.today().normalize()
17
- # Get last business day before today
18
- normalized_date = bday.offset_bdays(today, -1)
19
- else:
20
- raise ValueError("Invalid date format.")
21
-
22
- # Raise an error if the trade date is in the future
23
- if normalized_date > pd.Timestamp.today().normalize():
24
- raise ValueError("Trade date cannot be in the future.")
25
-
26
- # Raise error if the reference date is not a business day
27
- if not bday.is_bday(normalized_date):
28
- raise ValueError("Trade date must be a business day.")
29
-
30
- return normalized_date
31
-
32
-
33
- def get_expiration_date(expiration_code: str) -> pd.Timestamp:
34
- """
35
- Converts an expiration code into its corresponding expiration date.
36
-
37
- This function translates an expiration code into a specific expiration date based on
38
- a given mapping. The expiration code consists of a letter representing the month and
39
- two digits for the year. The function ensures the date returned is a valid business
40
- day by adjusting weekends and holidays as necessary.
41
-
42
- Args:
43
- expiration_code (str): The expiration code to be converted, where the first
44
- letter represents the month and the last two digits represent the year
45
- (e.g., "F23" for January 2023).
46
-
47
- Returns:
48
- pd.Timestamp: The expiration date corresponding to the code, adjusted to a valid
49
- business day. Returns pd.NaT if the code is invalid.
50
-
51
- Examples:
52
- >>> get_expiration_date("F23")
53
- pd.Timestamp('2023-01-01')
54
-
55
- >>> get_expiration_date("Z33")
56
- pd.Timestamp('2033-12-01')
57
-
58
- >>> get_expiration_date("A99")
59
- pd.NaT
60
-
61
- Notes:
62
- The expiration date is calculated based on the format change introduced by B3 on
63
- 22-05-2006, where the first letter represents the month and the last two digits
64
- represent the year.
65
- """
66
- month_codes = {
67
- "F": 1,
68
- "G": 2,
69
- "H": 3,
70
- "J": 4,
71
- "K": 5,
72
- "M": 6,
73
- "N": 7,
74
- "Q": 8,
75
- "U": 9,
76
- "V": 10,
77
- "X": 11,
78
- "Z": 12,
79
- }
80
-
81
- try:
82
- month_code = expiration_code[0]
83
- month = month_codes[month_code]
84
- year = int("20" + expiration_code[-2:])
85
- # The expiration date is always the first business day of the month
86
- expiration = pd.Timestamp(year, month, 1)
87
-
88
- # Adjust to the next business day when expiration date is a weekend or a holiday
89
- adj_expiration = bday.offset_bdays(expiration, offset=0)
90
-
91
- return adj_expiration
92
-
93
- except (KeyError, ValueError):
94
- return pd.NaT # type: ignore
95
-
96
-
97
- def fetch_data(
98
- trade_date: str | pd.Timestamp | None = None,
99
- source_type: Literal["bmf", "b3", "b3s"] = "bmf",
100
- return_raw: bool = False,
101
- ) -> pd.DataFrame:
102
- """
103
- Fetches DI futures data for a specified trade date from B3.
104
-
105
- Retrieves and processes DI futures data from B3 for a given trade date. This
106
- function serves as the primary method for accessing DI data, with options to
107
- specify the source of the data and whether to return raw data.
108
-
109
- Args:
110
- trade_date (str | pd.Timestamp | None, optional): The trade date for which to
111
- fetch DI data. If None or not provided, uses the previous business day.
112
- source_type (Literal["bmf", "b3", "b3s"], optional): Indicates the source of
113
- the data. Defaults to "bmf". Options include:
114
- - "bmf": Fetches data from the old BM&FBOVESPA website. Fastest option.
115
- - "b3": Fetches data from the complete Price Report (XML file) provided
116
- by B3.
117
- - "b3s": Fetches data from the simplified Price Report (XML file)
118
- provided by B3. Faster than "b3" but less detailed.
119
- return_raw (bool, optional): If True, returns the raw DI data without
120
- processing.
121
-
122
- Returns:
123
- pd.DataFrame: A DataFrame containing the DI futures data for the specified
124
- trade
125
- date. Format and content depend on the source_type and return_raw flag.
126
-
127
- Examples:
128
- # Fetch DI data for the previous business day using default settings
129
- >>> get_di()
130
-
131
- # Fetch DI data for a specific trade date from the simplified B3 Price Report
132
- >>> get_di("2023-12-28", source_type="b3s")
133
-
134
- Notes:
135
- - Complete Price Report XML files are about 5 MB in size.
136
- - Simplified Price Report XML files are significantly smaller, around 50 kB.
137
- - For file specifications, refer to the B3 documentation: [B3 File Specs](https://www.b3.com.br/data/files/16/70/29/9C/6219D710C8F297D7AC094EA8/Catalogo_precos_v1.3.pdf)
138
- """
139
- # Force trade_date to be a pandas pd.Timestamp
140
- normalized_trade_date = _normalize_date(trade_date)
141
-
142
- if source_type == "bmf":
143
- return web.get_di(normalized_trade_date, return_raw)
144
- elif source_type in ["b3", "b3s"]:
145
- return xml.read_xml(normalized_trade_date, source_type, return_raw)
146
- else:
147
- raise ValueError("source_type must be either 'bmf', 'b3' or 'b3s'.")
148
-
149
-
150
- def read_data(file_path: Path, return_raw: bool = False) -> pd.DataFrame:
151
- """
152
- Reads DI futures data from a file and returns it as a pandas DataFrame.
153
-
154
- This function opens and reads a DI futures data file, returning the contents as a
155
- pandas DataFrame. It supports reading from both XML files provided by B3, wich
156
- are the simplified and complete Price Reports.
157
-
158
- Args:
159
- file_path (Path): The file path to the DI data file. This should be a valid
160
- Path object pointing to the location of the file.
161
- return_raw (bool, optional): If set to True, the function returns the raw data
162
- without applying any transformation or processing. Useful for cases where
163
- raw data inspection or custom processing is needed. Defaults to False.
164
-
165
- Returns:
166
- pd.DataFrame: A DataFrame containing the processed or raw DI futures data,
167
- depending on the `return_raw` flag.
168
-
169
- Examples:
170
- >>> read_di(Path("path/to/di_data_file.xml"))
171
- # returns a DataFrame with the DI futures data
172
-
173
- >>> read_di(Path("path/to/di_data_file.xml"), return_raw=True)
174
- # returns a DataFrame with the raw DI futures data, without processing
175
-
176
- Note:
177
- The ability to process and return raw data is primarily intended for advanced
178
- users who require access to the data in its original form for custom analyses.
179
- """
180
- return xml.read_di(file_path, return_raw=return_raw)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -1,5 +1,5 @@
1
- import pandas as pd
2
1
  import numpy as np
2
+ import pandas as pd
3
3
 
4
4
  import pyield as yd
5
5