PYield 0.6.0__tar.gz → 0.7.1__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.
- {pyield-0.6.0 → pyield-0.7.1}/PKG-INFO +54 -39
- {pyield-0.6.0 → pyield-0.7.1}/README.md +53 -38
- pyield-0.7.1/pyield/__about__.py +1 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/__init__.py +3 -2
- pyield-0.7.1/pyield/data_access.py +89 -0
- pyield-0.7.1/pyield/futures/__init__.py +10 -0
- pyield-0.7.1/pyield/futures/common.py +175 -0
- pyield-0.7.1/pyield/futures/ddi.py +178 -0
- pyield-0.6.0/pyield/di/web.py → pyield-0.7.1/pyield/futures/di.py +16 -130
- pyield-0.6.0/pyield/di/xml.py → pyield-0.7.1/pyield/futures/di_xml.py +47 -7
- pyield-0.7.1/pyield/indicators.py +59 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/treasuries.py +7 -23
- pyield-0.7.1/pyield/utils.py +55 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyproject.toml +1 -1
- pyield-0.6.0/tests/test_di.py → pyield-0.7.1/tests/test_futures.py +11 -11
- pyield-0.6.0/pyield/__about__.py +0 -1
- pyield-0.6.0/pyield/data_access.py +0 -51
- pyield-0.6.0/pyield/di/__init__.py +0 -6
- pyield-0.6.0/pyield/di/core.py +0 -180
- pyield-0.6.0/pyield/utils.py +0 -26
- {pyield-0.6.0 → pyield-0.7.1}/LICENSE +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/bday.py +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/data_analysis.py +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/holidays/__init__.py +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/holidays/core.py +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/pyield/py.typed +0 -0
- {pyield-0.6.0 → pyield-0.7.1}/tests/__init__.py +0 -0
- pyield-0.6.0/tests/test_br_calendar.py → pyield-0.7.1/tests/test_bday.py +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PYield
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.7.1
|
|
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
|
|
|
@@ -67,29 +67,56 @@ pip install pyield
|
|
|
67
67
|
|
|
68
68
|
## How to use PYield
|
|
69
69
|
|
|
70
|
+
### Business Days Tools (Brazilian holidays are automatically considered)
|
|
71
|
+
```python
|
|
72
|
+
>>> import pyield as yd
|
|
73
|
+
|
|
74
|
+
# Count the number of business days between two dates
|
|
75
|
+
# Start date is included, end date is excluded
|
|
76
|
+
>>> yd.count_bdays(start='2023-12-29', end='2024-01-02')
|
|
77
|
+
1
|
|
78
|
+
|
|
79
|
+
# Get the next business day after a given date (offset=1)
|
|
80
|
+
>>> yd.offset_bdays(dates="2023-12-29", offset=1)
|
|
81
|
+
Timestamp('2024-01-02 00:00:00')
|
|
82
|
+
|
|
83
|
+
# Get the next business day if it is not a business day (offset=0)
|
|
84
|
+
>>> yd.offset_bdays(dates="2023-12-30", offset=0)
|
|
85
|
+
Timestamp('2024-01-02 00:00:00')
|
|
86
|
+
|
|
87
|
+
# Since 2023-12-29 is a business day, it returns the same date (offset=0)
|
|
88
|
+
>>> yd.offset_bdays(dates="2023-12-29", offset=0)
|
|
89
|
+
Timestamp('2023-12-29 00:00:00')
|
|
90
|
+
|
|
91
|
+
# Generate a pandas series with the business days between two dates
|
|
92
|
+
>>> yd.generate_bdays(start='2023-12-29', end='2024-01-03')
|
|
93
|
+
0 2023-12-29
|
|
94
|
+
1 2024-01-02
|
|
95
|
+
2 2024-01-03
|
|
96
|
+
dtype: datetime64[ns]
|
|
97
|
+
```
|
|
98
|
+
|
|
70
99
|
### Futures Data
|
|
71
100
|
```python
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
2024-03-08
|
|
79
|
-
|
|
80
|
-
2024-03-08
|
|
81
|
-
|
|
82
|
-
2024-03-08
|
|
83
|
-
2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
|
|
84
|
-
2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
|
|
101
|
+
# Fetch a DataFrame with the DI Futures data from B3
|
|
102
|
+
>>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
|
|
103
|
+
|
|
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
|
|
85
112
|
```
|
|
86
113
|
|
|
87
114
|
### Treasury Bonds Data
|
|
88
115
|
```python
|
|
89
|
-
#
|
|
116
|
+
# Fetch a DataFrame with the NTN-B data from ANBIMA
|
|
90
117
|
# Anbima data is available for the last 5 working days
|
|
91
118
|
# Obs: Anbima members have access to the full history
|
|
92
|
-
>>> yd.
|
|
119
|
+
>>> yd.fetch_asset(asset_code="NTN-B", reference_date='2024-04-12')
|
|
93
120
|
|
|
94
121
|
BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
|
|
95
122
|
NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
|
|
@@ -116,29 +143,17 @@ NTN-F 2024-04-11 2033-01-01 -5.69000
|
|
|
116
143
|
NTN-F 2024-04-11 2035-01-01 -1.27000
|
|
117
144
|
```
|
|
118
145
|
|
|
119
|
-
###
|
|
146
|
+
### Indicators Data
|
|
120
147
|
```python
|
|
121
|
-
#
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
1
|
|
148
|
+
# Fetch the SELIC target rate from the Central Bank of Brazil
|
|
149
|
+
>>> yd.fetch_indicator(indicator_code="SELIC", reference_date='2024-04-12')
|
|
150
|
+
10.75
|
|
125
151
|
|
|
126
|
-
#
|
|
127
|
-
>>> yd.
|
|
128
|
-
|
|
152
|
+
# Fetch the IPCA monthly inflation rate from IBGE
|
|
153
|
+
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-03-18')
|
|
154
|
+
0.16
|
|
129
155
|
|
|
130
|
-
#
|
|
131
|
-
>>> yd.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# Since 2023-12-29 is a business day, it returns the same date (offset=0)
|
|
135
|
-
>>> yd.offset_bdays(dates="2023-12-29", offset=0)
|
|
136
|
-
Timestamp('2023-12-29 00:00:00')
|
|
137
|
-
|
|
138
|
-
# Generate a pandas series with the business days between two dates
|
|
139
|
-
>>> yd.generate_bdays(start='2023-12-29', end='2024-01-03')
|
|
140
|
-
0 2023-12-29
|
|
141
|
-
1 2024-01-02
|
|
142
|
-
2 2024-01-03
|
|
143
|
-
dtype: datetime64[ns]
|
|
156
|
+
# If no data is yet available for the indicator, the function returns None
|
|
157
|
+
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
|
|
158
|
+
None
|
|
144
159
|
```
|
|
@@ -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
|
|
|
@@ -25,29 +25,56 @@ pip install pyield
|
|
|
25
25
|
|
|
26
26
|
## How to use PYield
|
|
27
27
|
|
|
28
|
+
### Business Days Tools (Brazilian holidays are automatically considered)
|
|
29
|
+
```python
|
|
30
|
+
>>> import pyield as yd
|
|
31
|
+
|
|
32
|
+
# Count the number of business days between two dates
|
|
33
|
+
# Start date is included, end date is excluded
|
|
34
|
+
>>> yd.count_bdays(start='2023-12-29', end='2024-01-02')
|
|
35
|
+
1
|
|
36
|
+
|
|
37
|
+
# Get the next business day after a given date (offset=1)
|
|
38
|
+
>>> yd.offset_bdays(dates="2023-12-29", offset=1)
|
|
39
|
+
Timestamp('2024-01-02 00:00:00')
|
|
40
|
+
|
|
41
|
+
# Get the next business day if it is not a business day (offset=0)
|
|
42
|
+
>>> yd.offset_bdays(dates="2023-12-30", offset=0)
|
|
43
|
+
Timestamp('2024-01-02 00:00:00')
|
|
44
|
+
|
|
45
|
+
# Since 2023-12-29 is a business day, it returns the same date (offset=0)
|
|
46
|
+
>>> yd.offset_bdays(dates="2023-12-29", offset=0)
|
|
47
|
+
Timestamp('2023-12-29 00:00:00')
|
|
48
|
+
|
|
49
|
+
# Generate a pandas series with the business days between two dates
|
|
50
|
+
>>> yd.generate_bdays(start='2023-12-29', end='2024-01-03')
|
|
51
|
+
0 2023-12-29
|
|
52
|
+
1 2024-01-02
|
|
53
|
+
2 2024-01-03
|
|
54
|
+
dtype: datetime64[ns]
|
|
55
|
+
```
|
|
56
|
+
|
|
28
57
|
### Futures Data
|
|
29
58
|
```python
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
2024-03-08
|
|
37
|
-
|
|
38
|
-
2024-03-08
|
|
39
|
-
|
|
40
|
-
2024-03-08
|
|
41
|
-
2024-03-08 F38 2038-01-04 3462 ... <NA> <NA> <NA> 10.859
|
|
42
|
-
2024-03-08 F39 2039-01-03 3713 ... <NA> <NA> <NA> 10.85
|
|
59
|
+
# Fetch a DataFrame with the DI Futures data from B3
|
|
60
|
+
>>> yd.fetch_asset(asset_code="DI1", reference_date='2024-03-08')
|
|
61
|
+
|
|
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
|
|
43
70
|
```
|
|
44
71
|
|
|
45
72
|
### Treasury Bonds Data
|
|
46
73
|
```python
|
|
47
|
-
#
|
|
74
|
+
# Fetch a DataFrame with the NTN-B data from ANBIMA
|
|
48
75
|
# Anbima data is available for the last 5 working days
|
|
49
76
|
# Obs: Anbima members have access to the full history
|
|
50
|
-
>>> yd.
|
|
77
|
+
>>> yd.fetch_asset(asset_code="NTN-B", reference_date='2024-04-12')
|
|
51
78
|
|
|
52
79
|
BondType ReferenceDate MaturityDate BidRate AskRate IndicativeRate Price
|
|
53
80
|
NTN-B 2024-04-12 2024-08-15 0.07540 0.07504 0.07523 4,271.43565
|
|
@@ -74,29 +101,17 @@ NTN-F 2024-04-11 2033-01-01 -5.69000
|
|
|
74
101
|
NTN-F 2024-04-11 2035-01-01 -1.27000
|
|
75
102
|
```
|
|
76
103
|
|
|
77
|
-
###
|
|
104
|
+
### Indicators Data
|
|
78
105
|
```python
|
|
79
|
-
#
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
1
|
|
106
|
+
# Fetch the SELIC target rate from the Central Bank of Brazil
|
|
107
|
+
>>> yd.fetch_indicator(indicator_code="SELIC", reference_date='2024-04-12')
|
|
108
|
+
10.75
|
|
83
109
|
|
|
84
|
-
#
|
|
85
|
-
>>> yd.
|
|
86
|
-
|
|
110
|
+
# Fetch the IPCA monthly inflation rate from IBGE
|
|
111
|
+
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-03-18')
|
|
112
|
+
0.16
|
|
87
113
|
|
|
88
|
-
#
|
|
89
|
-
>>> yd.
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
# Since 2023-12-29 is a business day, it returns the same date (offset=0)
|
|
93
|
-
>>> yd.offset_bdays(dates="2023-12-29", offset=0)
|
|
94
|
-
Timestamp('2023-12-29 00:00:00')
|
|
95
|
-
|
|
96
|
-
# Generate a pandas series with the business days between two dates
|
|
97
|
-
>>> yd.generate_bdays(start='2023-12-29', end='2024-01-03')
|
|
98
|
-
0 2023-12-29
|
|
99
|
-
1 2024-01-02
|
|
100
|
-
2 2024-01-03
|
|
101
|
-
dtype: datetime64[ns]
|
|
114
|
+
# If no data is yet available for the indicator, the function returns None
|
|
115
|
+
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
|
|
116
|
+
None
|
|
102
117
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.1"
|
|
@@ -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
|
|
3
|
+
from .data_access import fetch_asset, fetch_indicator
|
|
4
4
|
from .data_analysis import calculate_spreads
|
|
5
5
|
|
|
6
6
|
__all__ = [
|
|
@@ -9,6 +9,7 @@ __all__ = [
|
|
|
9
9
|
"generate_bdays",
|
|
10
10
|
"offset_bdays",
|
|
11
11
|
"count_bdays",
|
|
12
|
-
"
|
|
12
|
+
"fetch_asset",
|
|
13
|
+
"fetch_indicator",
|
|
13
14
|
"calculate_spreads",
|
|
14
15
|
]
|
|
@@ -0,0 +1,89 @@
|
|
|
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 previous 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
|
+
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)
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError("Asset type not supported.")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def fetch_indicator(
|
|
59
|
+
indicator_code: str,
|
|
60
|
+
reference_date: str | pd.Timestamp | None = None,
|
|
61
|
+
) -> float | None:
|
|
62
|
+
"""
|
|
63
|
+
Fetches data for a specified economic indicator and reference date.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
indicator_code (str): The code for the economic indicator. Supported options:
|
|
67
|
+
- "SELIC": SELIC target rate from the Central Bank of Brazil.
|
|
68
|
+
- "IPCA": IPCA monthly inflation rate from IBGE.
|
|
69
|
+
reference_date (str | pd.Timestamp | None): The reference date for which data is
|
|
70
|
+
fetched. Defaults to the previous business day if None.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
pd.Series: A Series containing the fetched data for the specified indicator.
|
|
74
|
+
|
|
75
|
+
Raises:
|
|
76
|
+
ValueError: If the indicator code is not recognized or supported.
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> fetch_indicator('SELIC', '2023-04-01')
|
|
80
|
+
>>> fetch_indicator('IPCA', '2023-04-01')
|
|
81
|
+
"""
|
|
82
|
+
normalized_date = _normalize_date(reference_date)
|
|
83
|
+
|
|
84
|
+
if indicator_code.lower() == "selic":
|
|
85
|
+
return it.fetch_selic_target(reference_date=normalized_date)
|
|
86
|
+
elif indicator_code.lower() == "ipca":
|
|
87
|
+
return it.fetch_ipca_mr(reference_date=normalized_date)
|
|
88
|
+
else:
|
|
89
|
+
raise ValueError("Indicator type not supported.")
|
|
@@ -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
|