PYield 0.7.7__tar.gz → 0.7.8__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.7.7 → pyield-0.7.8}/PKG-INFO +7 -7
- {pyield-0.7.7 → pyield-0.7.8}/README.md +5 -5
- pyield-0.7.8/pyield/__about__.py +1 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/data_access.py +22 -14
- pyield-0.7.8/pyield/futures/historical/__init__.py +6 -0
- {pyield-0.7.7/pyield/futures → pyield-0.7.8/pyield/futures/historical}/common.py +55 -86
- pyield-0.7.8/pyield/futures/historical/dap.py +62 -0
- {pyield-0.7.7/pyield/futures → pyield-0.7.8/pyield/futures/historical}/ddi.py +7 -28
- {pyield-0.7.7/pyield/futures → pyield-0.7.8/pyield/futures/historical}/di.py +7 -76
- {pyield-0.7.7/pyield/futures → pyield-0.7.8/pyield/futures/historical}/di_xml.py +2 -2
- {pyield-0.7.7/pyield/futures → pyield-0.7.8/pyield/futures/historical}/frc.py +7 -8
- pyield-0.7.8/pyield/futures/intraday/__init__.py +3 -0
- pyield-0.7.8/pyield/futures/intraday/trading_data.py +107 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/treasuries.py +2 -2
- {pyield-0.7.7 → pyield-0.7.8}/pyproject.toml +2 -2
- pyield-0.7.8/tests/__init__.py +0 -0
- pyield-0.7.8/tests/test_futures.py +38 -0
- pyield-0.7.7/pyield/__about__.py +0 -1
- pyield-0.7.7/pyield/futures/__init__.py +0 -13
- pyield-0.7.7/tests/test_futures.py +0 -80
- {pyield-0.7.7 → pyield-0.7.8}/LICENSE +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/__init__.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/bday.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/data_analysis.py +0 -0
- {pyield-0.7.7/tests → pyield-0.7.8/pyield/futures}/__init__.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/holidays/__init__.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/holidays/core.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/indicators.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/projections.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/py.typed +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/pyield/utils.py +0 -0
- {pyield-0.7.7 → pyield-0.7.8}/tests/test_bday.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PYield
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.8
|
|
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>
|
|
@@ -32,13 +32,13 @@ Classifier: Intended Audience :: Financial and Insurance Industry
|
|
|
32
32
|
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
33
33
|
Project-URL: Source, https://github.com/crdcj/PYield
|
|
34
34
|
Requires-Python: >=3.11
|
|
35
|
-
Requires-Dist: requests
|
|
36
35
|
Requires-Dist: pandas>=2.0.0
|
|
37
36
|
Requires-Dist: numpy
|
|
38
37
|
Requires-Dist: beautifulsoup4
|
|
39
38
|
Requires-Dist: html5lib
|
|
40
39
|
Requires-Dist: lxml
|
|
41
40
|
Requires-Dist: python-calamine>=0.2.0
|
|
41
|
+
Requires-Dist: requests>=2.31.0
|
|
42
42
|
Description-Content-Type: text/markdown
|
|
43
43
|
|
|
44
44
|
[](https://pypi.python.org/pypi/pyield)
|
|
@@ -101,7 +101,7 @@ dtype: datetime64[ns]
|
|
|
101
101
|
```python
|
|
102
102
|
# Fetch current DI Futures data from B3 (15 minutes delay)
|
|
103
103
|
>>> yd.fetch_asset(asset_code="DI1")
|
|
104
|
-
TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate
|
|
104
|
+
TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate CurrentRate
|
|
105
105
|
2024-04-21 13:37:39 DI1K24 2024-05-02 7 ... 0.10660 0.10652 0.10660 0.10660
|
|
106
106
|
2024-04-21 13:37:39 DI1M24 2024-06-03 28 ... 0.10518 0.10510 0.10516 0.10518
|
|
107
107
|
2024-04-21 13:37:39 DI1N24 2024-07-01 48 ... 0.10480 0.10456 0.10462 0.10460
|
|
@@ -158,11 +158,11 @@ BondType ReferenceDate MaturityDate DISpread
|
|
|
158
158
|
```python
|
|
159
159
|
# Fetch the SELIC target rate from the Central Bank of Brazil
|
|
160
160
|
>>> yd.fetch_indicator(indicator_code="SELIC", reference_date='2024-04-12')
|
|
161
|
-
10.75
|
|
161
|
+
0.1075 # 10.75%
|
|
162
162
|
|
|
163
163
|
# Fetch the IPCA monthly inflation rate from IBGE
|
|
164
164
|
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-03-18')
|
|
165
|
-
0.16
|
|
165
|
+
0.16 # 0.16%
|
|
166
166
|
|
|
167
167
|
# If no data is yet available for the indicator, the function returns None
|
|
168
168
|
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
|
|
@@ -178,9 +178,9 @@ IndicatorProjection(
|
|
|
178
178
|
last_updated=Timestamp('2024-04-19 18:55:00'),
|
|
179
179
|
reference_month_ts=Timestamp('2024-04-01 00:00:00'),
|
|
180
180
|
reference_month_br='ABR/2024',
|
|
181
|
-
projected_value=0.0035
|
|
181
|
+
projected_value=0.0035 # 0.35%
|
|
182
182
|
)
|
|
183
183
|
>>> ipca.projected_value
|
|
184
|
-
0.0035
|
|
184
|
+
0.0035 # 0.35%
|
|
185
185
|
```
|
|
186
186
|
|
|
@@ -58,7 +58,7 @@ dtype: datetime64[ns]
|
|
|
58
58
|
```python
|
|
59
59
|
# Fetch current DI Futures data from B3 (15 minutes delay)
|
|
60
60
|
>>> yd.fetch_asset(asset_code="DI1")
|
|
61
|
-
TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate
|
|
61
|
+
TradeTimestamp TickerSymbol ExpirationDate BDaysToExp ... MaxRate LastAskRate LastBidRate CurrentRate
|
|
62
62
|
2024-04-21 13:37:39 DI1K24 2024-05-02 7 ... 0.10660 0.10652 0.10660 0.10660
|
|
63
63
|
2024-04-21 13:37:39 DI1M24 2024-06-03 28 ... 0.10518 0.10510 0.10516 0.10518
|
|
64
64
|
2024-04-21 13:37:39 DI1N24 2024-07-01 48 ... 0.10480 0.10456 0.10462 0.10460
|
|
@@ -115,11 +115,11 @@ BondType ReferenceDate MaturityDate DISpread
|
|
|
115
115
|
```python
|
|
116
116
|
# Fetch the SELIC target rate from the Central Bank of Brazil
|
|
117
117
|
>>> yd.fetch_indicator(indicator_code="SELIC", reference_date='2024-04-12')
|
|
118
|
-
10.75
|
|
118
|
+
0.1075 # 10.75%
|
|
119
119
|
|
|
120
120
|
# Fetch the IPCA monthly inflation rate from IBGE
|
|
121
121
|
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-03-18')
|
|
122
|
-
0.16
|
|
122
|
+
0.16 # 0.16%
|
|
123
123
|
|
|
124
124
|
# If no data is yet available for the indicator, the function returns None
|
|
125
125
|
>>> yd.fetch_indicator(indicator_code="IPCA", reference_date='2024-04-10')
|
|
@@ -135,9 +135,9 @@ IndicatorProjection(
|
|
|
135
135
|
last_updated=Timestamp('2024-04-19 18:55:00'),
|
|
136
136
|
reference_month_ts=Timestamp('2024-04-01 00:00:00'),
|
|
137
137
|
reference_month_br='ABR/2024',
|
|
138
|
-
projected_value=0.0035
|
|
138
|
+
projected_value=0.0035 # 0.35%
|
|
139
139
|
)
|
|
140
140
|
>>> ipca.projected_value
|
|
141
|
-
0.0035
|
|
141
|
+
0.0035 # 0.35%
|
|
142
142
|
```
|
|
143
143
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.8"
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
-
from . import futures as ft
|
|
4
3
|
from . import indicators as it
|
|
5
4
|
from . import projections as pr
|
|
6
5
|
from . import treasuries as tr
|
|
6
|
+
from .futures import historical as fh
|
|
7
|
+
from .futures import intraday as fi
|
|
7
8
|
from .utils import _normalize_date
|
|
8
9
|
|
|
9
10
|
|
|
@@ -24,6 +25,7 @@ def fetch_asset(
|
|
|
24
25
|
- "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
|
|
25
26
|
- "DDI": DI x U.S. Dollar Spread Futures (Futuro de Cupom Cambial) from B3.
|
|
26
27
|
- "FRC": Forward Rate Agreement (FRA) from B3.
|
|
28
|
+
- "DAP": DI x IPCA Spread Futures
|
|
27
29
|
reference_date (str | pd.Timestamp | None): The reference date for which data is
|
|
28
30
|
fetched. Defaults to the last business day if None.
|
|
29
31
|
**kwargs: Additional keyword arguments, specifically:
|
|
@@ -43,24 +45,30 @@ def fetch_asset(
|
|
|
43
45
|
return_raw = kwargs.get("return_raw", False)
|
|
44
46
|
normalized_date = _normalize_date(reference_date)
|
|
45
47
|
|
|
48
|
+
today = pd.Timestamp.today().normalize()
|
|
49
|
+
if normalized_date == today:
|
|
50
|
+
return fi.fetch_intraday(future_code=asset_code.upper())
|
|
51
|
+
|
|
46
52
|
if asset_code.lower() == "trb":
|
|
47
53
|
return tr.fetch_bonds(reference_date=normalized_date, return_raw=return_raw)
|
|
48
|
-
|
|
54
|
+
|
|
55
|
+
if asset_code.lower() in ["ltn", "lft", "ntn-f", "ntn-b"]:
|
|
49
56
|
df = tr.fetch_bonds(reference_date=normalized_date)
|
|
50
57
|
return df.query(f"BondType == '{asset_code.upper()}'")
|
|
51
58
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
return
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
if asset_code.lower() == "di1":
|
|
60
|
+
return fh.fetch_di(trade_date=normalized_date, return_raw=return_raw)
|
|
61
|
+
|
|
62
|
+
if asset_code.lower() == "ddi":
|
|
63
|
+
return fh.fetch_ddi(trade_date=normalized_date, return_raw=return_raw)
|
|
64
|
+
|
|
65
|
+
if asset_code.lower() == "frc":
|
|
66
|
+
return fh.fetch_frc(trade_date=normalized_date, return_raw=return_raw)
|
|
67
|
+
|
|
68
|
+
if asset_code.lower() == "dap":
|
|
69
|
+
return fh.fetch_dap(trade_date=normalized_date, return_raw=return_raw)
|
|
70
|
+
|
|
71
|
+
raise ValueError("Asset type not supported.")
|
|
64
72
|
|
|
65
73
|
|
|
66
74
|
def fetch_indicator(
|
|
@@ -3,29 +3,7 @@ import io
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
|
-
from
|
|
7
|
-
|
|
8
|
-
COLUMNS_MAPPING = {
|
|
9
|
-
"VENCTO": "ExpirationCode",
|
|
10
|
-
"CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
|
|
11
|
-
"CONTR. FECH.(2)": "OpenContractsEndSession", # At the end of the day
|
|
12
|
-
"NÚM. NEGOC.": "TradeCount",
|
|
13
|
-
"CONTR. NEGOC.": "TradeVolume",
|
|
14
|
-
"VOL.": "FinancialVolume",
|
|
15
|
-
"AJUSTE": "SettlementPrice",
|
|
16
|
-
"AJUSTE ANTER. (3)": "PrevSettlementRate",
|
|
17
|
-
"AJUSTE CORRIG. (4)": "AdjSettlementRate",
|
|
18
|
-
"AJUSTE DE REF.": "SettlementRate", # FRC
|
|
19
|
-
"PREÇO MÍN.": "MinRate",
|
|
20
|
-
"PREÇO MÉD.": "AvgRate",
|
|
21
|
-
"PREÇO MÁX.": "MaxRate",
|
|
22
|
-
"PREÇO ABERTU.": "FirstRate",
|
|
23
|
-
"ÚLT. PREÇO": "CloseRate",
|
|
24
|
-
"VAR. PTOS.": "PointsVariation",
|
|
25
|
-
# Attention: bid/ask rates are inverted
|
|
26
|
-
"ÚLT.OF. COMPRA": "CloseAskRate",
|
|
27
|
-
"ÚLT.OF. VENDA": "CloseBidRate",
|
|
28
|
-
}
|
|
6
|
+
from ... import bday
|
|
29
7
|
|
|
30
8
|
|
|
31
9
|
def get_expiration_date(expiration_code: str) -> pd.Timestamp:
|
|
@@ -153,7 +131,28 @@ def get_old_expiration_date(
|
|
|
153
131
|
return pd.NaT # type: ignore
|
|
154
132
|
|
|
155
133
|
|
|
156
|
-
def
|
|
134
|
+
def convert_prices_to_rates(
|
|
135
|
+
prices: pd.Series, days_to_expiration: pd.Series, count_convention: int
|
|
136
|
+
) -> pd.Series:
|
|
137
|
+
"""
|
|
138
|
+
Internal function to convert DI futures prices to rates.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
prices (pd.Series): The futures prices to be converted.
|
|
142
|
+
days_to_expiration (pd.Series): The number of days to expiration for each price.
|
|
143
|
+
count_convention (int): The count convention for the DI futures contract.
|
|
144
|
+
Normally, it is 252 business days or 360 calendar days.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
pd.Series: A pd.Series containing the futures rates.
|
|
148
|
+
"""
|
|
149
|
+
rates = (100_000 / prices) ** (count_convention / days_to_expiration) - 1
|
|
150
|
+
|
|
151
|
+
# Round to 5 (3 in %) dec. places (contract's current max. precision)
|
|
152
|
+
return rates.round(5)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def fetch_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
157
156
|
"""
|
|
158
157
|
Fetch the historical futures data from B3 for a specific trade date. If the data is
|
|
159
158
|
not available, an empty DataFrame is returned.
|
|
@@ -199,60 +198,7 @@ def fetch_past_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame
|
|
|
199
198
|
return df
|
|
200
199
|
|
|
201
200
|
|
|
202
|
-
def
|
|
203
|
-
"""
|
|
204
|
-
Fetch the latest data for a given future code from B3 derivatives quotation API.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
future_code (str): The future code to fetch data for.
|
|
208
|
-
|
|
209
|
-
Returns:
|
|
210
|
-
pd.DataFrame: A DataFrame containing the normalized and cleaned data from the API.
|
|
211
|
-
|
|
212
|
-
Raises:
|
|
213
|
-
Exception: An exception is raised if the data fetch operation fails.
|
|
214
|
-
"""
|
|
215
|
-
|
|
216
|
-
url = f"https://cotacao.b3.com.br/mds/api/v1/DerivativeQuotation/{future_code}"
|
|
217
|
-
|
|
218
|
-
try:
|
|
219
|
-
r = requests.get(url)
|
|
220
|
-
r.raise_for_status() # Check for HTTP request errors
|
|
221
|
-
except requests.exceptions.RequestException:
|
|
222
|
-
raise Exception(f"Failed to fetch data for {future_code}.") from None
|
|
223
|
-
|
|
224
|
-
r.encoding = "utf-8" # Explicitly set response encoding to utf-8 for consistency
|
|
225
|
-
|
|
226
|
-
# Normalize JSON response into a flat table
|
|
227
|
-
df = pd.json_normalize(r.json()["Scty"])
|
|
228
|
-
|
|
229
|
-
# Clean and reformat the DataFrame columns
|
|
230
|
-
df.columns = (df.columns
|
|
231
|
-
.str.replace("SctyQtn.", "")
|
|
232
|
-
.str.replace("asset.AsstSummry.", "")
|
|
233
|
-
) # fmt: skip
|
|
234
|
-
df.drop(columns=["desc", "asset.code", "mkt.cd"], inplace=True)
|
|
235
|
-
|
|
236
|
-
# Convert maturity codes to datetime and drop rows with missing values
|
|
237
|
-
df["mtrtyCode"] = pd.to_datetime(df["mtrtyCode"], errors="coerce")
|
|
238
|
-
df.dropna(subset=["mtrtyCode"], inplace=True)
|
|
239
|
-
|
|
240
|
-
# Sort the DataFrame by maturity code and reset the index
|
|
241
|
-
df.sort_values("mtrtyCode", inplace=True, ignore_index=True)
|
|
242
|
-
|
|
243
|
-
# Get current date and time
|
|
244
|
-
now = pd.Timestamp.now().round("s")
|
|
245
|
-
# Subtract 15 minutes from the current time to account for API delay
|
|
246
|
-
trade_ts = now - pd.Timedelta(minutes=15)
|
|
247
|
-
df["TradeTimestamp"] = trade_ts
|
|
248
|
-
|
|
249
|
-
# Convert DataFrame to use nullable data types for better type consistency
|
|
250
|
-
df = df.convert_dtypes(dtype_backend="numpy_nullable")
|
|
251
|
-
|
|
252
|
-
return df
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
def rename_columns(df: pd.DataFrame):
|
|
201
|
+
def rename_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
256
202
|
all_columns = {
|
|
257
203
|
"VENCTO": "ExpirationCode",
|
|
258
204
|
"CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
|
|
@@ -274,13 +220,39 @@ def rename_columns(df: pd.DataFrame):
|
|
|
274
220
|
"ÚLT.OF. COMPRA": "CloseAskRate",
|
|
275
221
|
"ÚLT.OF. VENDA": "CloseBidRate",
|
|
276
222
|
}
|
|
277
|
-
rename_dict = {}
|
|
278
|
-
for col in all_columns:
|
|
279
|
-
if col in df.columns:
|
|
280
|
-
rename_dict[col] = all_columns[col]
|
|
223
|
+
rename_dict = {c: all_columns[c] for c in all_columns if c in df.columns}
|
|
281
224
|
return df.rename(columns=rename_dict)
|
|
282
225
|
|
|
283
226
|
|
|
227
|
+
def process_raw_df(
|
|
228
|
+
df: pd.DataFrame, trade_date: pd.Timestamp, asset_code: str
|
|
229
|
+
) -> pd.DataFrame:
|
|
230
|
+
df = rename_columns(df)
|
|
231
|
+
|
|
232
|
+
df["TradeDate"] = trade_date
|
|
233
|
+
# Convert to datetime64[ns] since it is pandas default type for timestamps
|
|
234
|
+
df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
|
|
235
|
+
|
|
236
|
+
df["TickerSymbol"] = asset_code + df["ExpirationCode"]
|
|
237
|
+
|
|
238
|
+
# Contract code format was changed in 22/05/2006
|
|
239
|
+
if trade_date < pd.Timestamp("2006-05-22"):
|
|
240
|
+
df["ExpirationDate"] = df["ExpirationCode"].apply(
|
|
241
|
+
get_old_expiration_date, args=(trade_date,)
|
|
242
|
+
)
|
|
243
|
+
else:
|
|
244
|
+
df["ExpirationDate"] = df["ExpirationCode"].apply(get_expiration_date)
|
|
245
|
+
|
|
246
|
+
# Columns where 0 means NaN
|
|
247
|
+
cols_with_nan = [col for col in df.columns if "Rate" in col]
|
|
248
|
+
if "SettlementPrice" in df.columns:
|
|
249
|
+
cols_with_nan.append("SettlementPrice")
|
|
250
|
+
# Replace 0 with NaN in these columns
|
|
251
|
+
df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
|
|
252
|
+
|
|
253
|
+
return df
|
|
254
|
+
|
|
255
|
+
|
|
284
256
|
def reorder_columns(df: pd.DataFrame):
|
|
285
257
|
all_columns = [
|
|
286
258
|
"TradeDate",
|
|
@@ -305,8 +277,5 @@ def reorder_columns(df: pd.DataFrame):
|
|
|
305
277
|
"CloseBidRate",
|
|
306
278
|
"CloseRate",
|
|
307
279
|
]
|
|
308
|
-
reordered_columns = []
|
|
309
|
-
for col in all_columns:
|
|
310
|
-
if col in df.columns:
|
|
311
|
-
reordered_columns.append(col)
|
|
280
|
+
reordered_columns = [col for col in all_columns if col in df.columns]
|
|
312
281
|
return df[reordered_columns]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from ... import bday as bd
|
|
4
|
+
from . import common as cm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _process_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
8
|
+
"""
|
|
9
|
+
Internal function to process and transform raw DI futures data.
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
df (pd.DataFrame): the raw DI DataFrame.
|
|
13
|
+
trade_date: a datetime-like object representing the trade date.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
|
|
17
|
+
"""
|
|
18
|
+
df["BDaysToExp"] = bd.count_bdays(trade_date, df["ExpirationDate"])
|
|
19
|
+
|
|
20
|
+
# Remove expired contracts
|
|
21
|
+
df.query("BDaysToExp > 0", inplace=True)
|
|
22
|
+
|
|
23
|
+
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
24
|
+
# Remove % and round to 5 (3 in %) dec. places in rate columns
|
|
25
|
+
df[rate_cols] = df[rate_cols].div(100).round(5)
|
|
26
|
+
|
|
27
|
+
# Calculate SettlementRate
|
|
28
|
+
df["SettlementRate"] = cm.convert_prices_to_rates(
|
|
29
|
+
prices=df["SettlementPrice"],
|
|
30
|
+
days_to_expiration=df["BDaysToExp"],
|
|
31
|
+
count_convention=252,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
return df
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def fetch_dap(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
|
|
38
|
+
"""
|
|
39
|
+
Fetchs the DI futures data for a given date from B3.
|
|
40
|
+
|
|
41
|
+
This function fetches and processes the DI futures data from B3 for a specific
|
|
42
|
+
trade date. It's the primary external interface for accessing DI data. If the data
|
|
43
|
+
is not available, an empty DataFrame is returned.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
|
|
47
|
+
return_raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
|
|
48
|
+
Defaults to False.
|
|
49
|
+
|
|
50
|
+
Returns:
|
|
51
|
+
pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
|
|
52
|
+
|
|
53
|
+
Notes:
|
|
54
|
+
- BDaysToExp: number of business days to ExpirationDate.
|
|
55
|
+
- OpenContracts: number of open contracts at the start of the trading day.
|
|
56
|
+
"""
|
|
57
|
+
df_raw = cm.fetch_raw_df(asset_code="DI1", trade_date=trade_date)
|
|
58
|
+
if return_raw or df_raw.empty:
|
|
59
|
+
return df_raw
|
|
60
|
+
df = cm.process_raw_df(df_raw, trade_date, asset_code="DI1")
|
|
61
|
+
df = _process_df(df, trade_date)
|
|
62
|
+
return cm.reorder_columns(df)
|
|
@@ -19,7 +19,7 @@ def _convert_prices_to_rates(prices: pd.Series, n_days: pd.Series) -> pd.Series:
|
|
|
19
19
|
return rates.round(5)
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
def
|
|
22
|
+
def _process_ddi_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
23
23
|
"""
|
|
24
24
|
Internal function to process and transform raw DI futures data.
|
|
25
25
|
|
|
@@ -30,22 +30,8 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
30
30
|
Returns:
|
|
31
31
|
pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
|
|
32
32
|
"""
|
|
33
|
-
df = cm.rename_columns(df)
|
|
34
|
-
|
|
35
|
-
df["TradeDate"] = trade_date
|
|
36
|
-
# Convert to datetime64[ns] since it is pandas default type for timestamps
|
|
37
|
-
df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
|
|
38
|
-
|
|
39
|
-
# Contract code format was changed in 22/05/2006
|
|
40
|
-
if trade_date < pd.Timestamp("2006-05-22"):
|
|
41
|
-
df["ExpirationDate"] = df["ExpirationCode"].apply(
|
|
42
|
-
cm.get_old_expiration_date, args=(trade_date,)
|
|
43
|
-
)
|
|
44
|
-
else:
|
|
45
|
-
df["ExpirationDate"] = df["ExpirationCode"].apply(cm.get_expiration_date)
|
|
46
|
-
|
|
47
33
|
df["DaysToExp"] = (df["ExpirationDate"] - trade_date).dt.days
|
|
48
|
-
# Convert to nullable integer, since
|
|
34
|
+
# Convert to nullable integer, since it is the default type in the library
|
|
49
35
|
df["DaysToExp"] = df["DaysToExp"].astype(pd.Int64Dtype())
|
|
50
36
|
|
|
51
37
|
# Remove expired contracts
|
|
@@ -53,10 +39,6 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
53
39
|
|
|
54
40
|
# Columns where 0 means NaN
|
|
55
41
|
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
56
|
-
cols_with_nan = rate_cols + ["SettlementPrice"]
|
|
57
|
-
# Replace 0 with NaN in these columns
|
|
58
|
-
df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
|
|
59
|
-
|
|
60
42
|
df[rate_cols] = df[rate_cols].div(100).round(5)
|
|
61
43
|
|
|
62
44
|
# Calculate SettlementRate
|
|
@@ -64,15 +46,10 @@ def _process_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
64
46
|
df["SettlementPrice"], df["DaysToExp"]
|
|
65
47
|
)
|
|
66
48
|
|
|
67
|
-
df["TickerSymbol"] = "DI1" + df["ExpirationCode"]
|
|
68
|
-
|
|
69
|
-
# Filter and order columns
|
|
70
|
-
df = cm.reorder_columns(df)
|
|
71
|
-
|
|
72
49
|
return df
|
|
73
50
|
|
|
74
51
|
|
|
75
|
-
def
|
|
52
|
+
def fetch_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
|
|
76
53
|
"""
|
|
77
54
|
Fetchs the DDI futures data for a given date from B3.
|
|
78
55
|
|
|
@@ -95,7 +72,9 @@ def fetch_past_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Dat
|
|
|
95
72
|
- DaysToExp: number of business days to ExpirationDate.
|
|
96
73
|
- OpenContracts: number of open contracts at the start of the trading day.
|
|
97
74
|
"""
|
|
98
|
-
df_raw = cm.
|
|
75
|
+
df_raw = cm.fetch_raw_df(asset_code="DDI", trade_date=trade_date)
|
|
99
76
|
if return_raw or df_raw.empty:
|
|
100
77
|
return df_raw
|
|
101
|
-
|
|
78
|
+
df = cm.process_raw_df(df_raw, trade_date, asset_code="DDI")
|
|
79
|
+
df = _process_ddi_df(df, trade_date)
|
|
80
|
+
return cm.reorder_columns(df)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
-
from
|
|
3
|
+
from ... import bday as bd
|
|
4
4
|
from . import common as cm
|
|
5
5
|
|
|
6
6
|
|
|
@@ -31,7 +31,7 @@ def _adjust_older_contracts_rates(df: pd.DataFrame, rate_cols: list) -> pd.DataF
|
|
|
31
31
|
return df
|
|
32
32
|
|
|
33
33
|
|
|
34
|
-
def
|
|
34
|
+
def _process_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
35
35
|
"""
|
|
36
36
|
Internal function to process and transform raw DI futures data.
|
|
37
37
|
|
|
@@ -42,31 +42,12 @@ def _process_past_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataF
|
|
|
42
42
|
Returns:
|
|
43
43
|
pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
|
|
44
44
|
"""
|
|
45
|
-
df = cm.rename_columns(df)
|
|
46
|
-
|
|
47
|
-
df["TradeDate"] = trade_date
|
|
48
|
-
# Convert to datetime64[ns] since it is pandas default type for timestamps
|
|
49
|
-
df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
|
|
50
|
-
|
|
51
|
-
# Contract code format was changed in 22/05/2006
|
|
52
|
-
if trade_date < pd.Timestamp("2006-05-22"):
|
|
53
|
-
df["ExpirationDate"] = df["ExpirationCode"].apply(
|
|
54
|
-
cm.get_old_expiration_date, args=(trade_date,)
|
|
55
|
-
)
|
|
56
|
-
else:
|
|
57
|
-
df["ExpirationDate"] = df["ExpirationCode"].apply(cm.get_expiration_date)
|
|
58
|
-
|
|
59
45
|
df["BDaysToExp"] = bd.count_bdays(trade_date, df["ExpirationDate"])
|
|
60
46
|
|
|
61
47
|
# Remove expired contracts
|
|
62
48
|
df.query("BDaysToExp > 0", inplace=True)
|
|
63
49
|
|
|
64
|
-
# Columns where 0 means NaN
|
|
65
50
|
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
66
|
-
cols_with_nan = rate_cols + ["SettlementPrice"]
|
|
67
|
-
# Replace 0 with NaN in these columns
|
|
68
|
-
df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
|
|
69
|
-
|
|
70
51
|
# Prior to 17/01/2002 (inclusive), prices were not converted to rates
|
|
71
52
|
if trade_date > pd.Timestamp("2002-01-17"):
|
|
72
53
|
# Remove % and round to 5 (3 in %) dec. places in rate columns
|
|
@@ -79,62 +60,10 @@ def _process_past_raw_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataF
|
|
|
79
60
|
df["SettlementPrice"], df["BDaysToExp"]
|
|
80
61
|
)
|
|
81
62
|
|
|
82
|
-
df["TickerSymbol"] = "DI1" + df["ExpirationCode"]
|
|
83
|
-
|
|
84
|
-
# Filter and order columns
|
|
85
|
-
df = cm.reorder_columns(df)
|
|
86
63
|
return df
|
|
87
64
|
|
|
88
65
|
|
|
89
|
-
def
|
|
90
|
-
df = raw_df.copy()
|
|
91
|
-
|
|
92
|
-
# Columns to be renamed
|
|
93
|
-
rename_columns = {
|
|
94
|
-
"TradeTimestamp": "TradeTimestamp",
|
|
95
|
-
"symb": "TickerSymbol",
|
|
96
|
-
"mtrtyCode": "ExpirationDate",
|
|
97
|
-
"BDaysToExp": "BDaysToExp",
|
|
98
|
-
"opnCtrcts": "OpenContracts",
|
|
99
|
-
"tradQty": "TradeCount",
|
|
100
|
-
"traddCtrctsQty": "TradeVolume",
|
|
101
|
-
"grssAmt": "FinancialVolume",
|
|
102
|
-
"prvsDayAdjstmntPric": "PrevSettlementRate",
|
|
103
|
-
"bottomLmtPric": "MinLimitRate",
|
|
104
|
-
"topLmtPric": "MaxLimitRate",
|
|
105
|
-
"opngPric": "OpenRate",
|
|
106
|
-
"minPric": "MinRate",
|
|
107
|
-
"avrgPric": "AvgRate",
|
|
108
|
-
"maxPric": "MaxRate",
|
|
109
|
-
"buyOffer.price": "LastAskRate",
|
|
110
|
-
"sellOffer.price": "LastBidRate",
|
|
111
|
-
"curPrc": "LastRate",
|
|
112
|
-
}
|
|
113
|
-
# Rename columns
|
|
114
|
-
df = df.rename(columns=rename_columns)
|
|
115
|
-
|
|
116
|
-
df["BDaysToExp"] = bd.count_bdays(df["TradeTimestamp"], df["ExpirationDate"])
|
|
117
|
-
|
|
118
|
-
# Remove percentage in all rate columns
|
|
119
|
-
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
120
|
-
df[rate_cols] = df[rate_cols] / 100
|
|
121
|
-
|
|
122
|
-
# Reorder columns based on the order of the dictionary
|
|
123
|
-
return df[rename_columns.values()]
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
def fetch_last_di() -> pd.DataFrame:
|
|
127
|
-
"""
|
|
128
|
-
Fetch the latest DI futures data from B3.
|
|
129
|
-
|
|
130
|
-
Returns:
|
|
131
|
-
pd.DataFrame: A Pandas pd.DataFrame containing the latest DI futures data.
|
|
132
|
-
"""
|
|
133
|
-
raw_df = cm.fetch_last_raw_df(future_code="DI1")
|
|
134
|
-
return _process_last_raw_di_df(raw_df)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
def fetch_past_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
|
|
66
|
+
def fetch_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
|
|
138
67
|
"""
|
|
139
68
|
Fetchs the DI futures data for a given date from B3.
|
|
140
69
|
|
|
@@ -154,7 +83,9 @@ def fetch_past_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Data
|
|
|
154
83
|
- BDaysToExp: number of business days to ExpirationDate.
|
|
155
84
|
- OpenContracts: number of open contracts at the start of the trading day.
|
|
156
85
|
"""
|
|
157
|
-
df_raw = cm.
|
|
86
|
+
df_raw = cm.fetch_raw_df(asset_code="DI1", trade_date=trade_date)
|
|
158
87
|
if return_raw or df_raw.empty:
|
|
159
88
|
return df_raw
|
|
160
|
-
|
|
89
|
+
df = cm.process_raw_df(df_raw, trade_date, asset_code="DI1")
|
|
90
|
+
df = _process_df(df, trade_date)
|
|
91
|
+
return cm.reorder_columns(df)
|
|
@@ -7,7 +7,7 @@ import requests
|
|
|
7
7
|
from lxml import etree
|
|
8
8
|
from pandas import DataFrame, Timestamp
|
|
9
9
|
|
|
10
|
-
from
|
|
10
|
+
from ... import bday
|
|
11
11
|
from . import common as cm
|
|
12
12
|
|
|
13
13
|
|
|
@@ -206,7 +206,7 @@ def _standardize_column_names(df: DataFrame) -> DataFrame:
|
|
|
206
206
|
"TradAvrgPric": "AvgRate",
|
|
207
207
|
"MaxPric": "MaxRate",
|
|
208
208
|
"FrstPric": "FirstRate",
|
|
209
|
-
"LastPric": "
|
|
209
|
+
"LastPric": "CloseRate",
|
|
210
210
|
"AdjstdQtTax": "SettlementRate",
|
|
211
211
|
# "RglrTxsQty"
|
|
212
212
|
# "RglrTraddCtrcts"
|
|
@@ -3,7 +3,7 @@ import pandas as pd
|
|
|
3
3
|
from . import common as cm
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def
|
|
6
|
+
def _process_frc_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
7
7
|
"""
|
|
8
8
|
Internal function to process and transform raw DI futures data.
|
|
9
9
|
|
|
@@ -33,14 +33,10 @@ def _process_past_data(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFra
|
|
|
33
33
|
# Round to 5 decimal places (3 in %) since it is the contract's precision
|
|
34
34
|
df[rate_cols] = df[rate_cols].replace(0, pd.NA).div(100).round(5)
|
|
35
35
|
|
|
36
|
-
df["TickerSymbol"] = "FRC" + df["ExpirationCode"]
|
|
37
|
-
|
|
38
|
-
# Filter and order columns
|
|
39
|
-
df = cm.reorder_columns(df)
|
|
40
36
|
return df
|
|
41
37
|
|
|
42
38
|
|
|
43
|
-
def
|
|
39
|
+
def fetch_frc(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
|
|
44
40
|
"""
|
|
45
41
|
Fetchs the DI futures data for a given date from B3.
|
|
46
42
|
|
|
@@ -55,7 +51,10 @@ def fetch_past_frc(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.Dat
|
|
|
55
51
|
Returns:
|
|
56
52
|
pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
|
|
57
53
|
"""
|
|
58
|
-
df_raw = cm.
|
|
54
|
+
df_raw = cm.fetch_raw_df(asset_code="FRC", trade_date=trade_date)
|
|
59
55
|
if return_raw or df_raw.empty:
|
|
60
56
|
return df_raw
|
|
61
|
-
|
|
57
|
+
# Filter and order columns
|
|
58
|
+
df = cm.process_raw_df(df_raw, trade_date, asset_code="FRC")
|
|
59
|
+
df = _process_frc_df(df, trade_date)
|
|
60
|
+
return cm.reorder_columns(df)
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _fetch_raw_df(future_code: str) -> pd.DataFrame:
|
|
6
|
+
"""
|
|
7
|
+
Fetch the latest data for a given future code from B3 derivatives quotation API.
|
|
8
|
+
|
|
9
|
+
Args:
|
|
10
|
+
future_code (str): The future code to fetch data for.
|
|
11
|
+
|
|
12
|
+
Returns:
|
|
13
|
+
pd.DataFrame: A DataFrame containing the normalized and cleaned data from the API.
|
|
14
|
+
|
|
15
|
+
Raises:
|
|
16
|
+
Exception: An exception is raised if the data fetch operation fails.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
url = f"https://cotacao.b3.com.br/mds/api/v1/DerivativeQuotation/{future_code}"
|
|
20
|
+
|
|
21
|
+
try:
|
|
22
|
+
r = requests.get(url)
|
|
23
|
+
r.raise_for_status() # Check for HTTP request errors
|
|
24
|
+
except requests.exceptions.RequestException:
|
|
25
|
+
raise Exception(f"Failed to fetch data for {future_code}.") from None
|
|
26
|
+
|
|
27
|
+
r.encoding = "utf-8" # Explicitly set response encoding to utf-8 for consistency
|
|
28
|
+
|
|
29
|
+
if "Quotation not available" in r.text:
|
|
30
|
+
return pd.DataFrame()
|
|
31
|
+
|
|
32
|
+
# Normalize JSON response into a flat table
|
|
33
|
+
df = pd.json_normalize(r.json()["Scty"])
|
|
34
|
+
|
|
35
|
+
# Clean and reformat the DataFrame columns
|
|
36
|
+
df.columns = (df.columns
|
|
37
|
+
.str.replace("SctyQtn.", "")
|
|
38
|
+
.str.replace("asset.AsstSummry.", "")
|
|
39
|
+
) # fmt: skip
|
|
40
|
+
df.drop(columns=["desc", "asset.code", "mkt.cd"], inplace=True)
|
|
41
|
+
|
|
42
|
+
# Convert maturity codes to datetime and drop rows with missing values
|
|
43
|
+
df["mtrtyCode"] = pd.to_datetime(df["mtrtyCode"], errors="coerce")
|
|
44
|
+
df.dropna(subset=["mtrtyCode"], inplace=True)
|
|
45
|
+
|
|
46
|
+
# Sort the DataFrame by maturity code and reset the index
|
|
47
|
+
df.sort_values("mtrtyCode", inplace=True, ignore_index=True)
|
|
48
|
+
|
|
49
|
+
# Get current date and time
|
|
50
|
+
now = pd.Timestamp.now().round("s")
|
|
51
|
+
# Subtract 15 minutes from the current time to account for API delay
|
|
52
|
+
trade_ts = now - pd.Timedelta(minutes=15)
|
|
53
|
+
df["TradeTimestamp"] = trade_ts
|
|
54
|
+
|
|
55
|
+
# Convert DataFrame to use nullable data types for better type consistency
|
|
56
|
+
df = df.convert_dtypes(dtype_backend="numpy_nullable")
|
|
57
|
+
|
|
58
|
+
return df
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _process_raw_df(raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
62
|
+
df = raw_df.copy()
|
|
63
|
+
|
|
64
|
+
# Columns to be renamed
|
|
65
|
+
all_columns = {
|
|
66
|
+
"TradeTimestamp": "TradeTimestamp",
|
|
67
|
+
"symb": "TickerSymbol",
|
|
68
|
+
"mtrtyCode": "ExpirationDate",
|
|
69
|
+
"BDaysToExp": "BDaysToExp",
|
|
70
|
+
"opnCtrcts": "OpenContracts",
|
|
71
|
+
"tradQty": "TradeCount",
|
|
72
|
+
"traddCtrctsQty": "TradeVolume",
|
|
73
|
+
"grssAmt": "FinancialVolume",
|
|
74
|
+
"prvsDayAdjstmntPric": "PrevSettlementRate",
|
|
75
|
+
"bottomLmtPric": "MinLimitRate",
|
|
76
|
+
"topLmtPric": "MaxLimitRate",
|
|
77
|
+
"opngPric": "OpenRate",
|
|
78
|
+
"minPric": "MinRate",
|
|
79
|
+
"avrgPric": "AvgRate",
|
|
80
|
+
"maxPric": "MaxRate",
|
|
81
|
+
"buyOffer.price": "CurrentAskRate",
|
|
82
|
+
"sellOffer.price": "CurrentBidRate",
|
|
83
|
+
"curPrc": "CurrentRate",
|
|
84
|
+
}
|
|
85
|
+
# Check which columns are present in the DataFrame before renaming
|
|
86
|
+
rename_dict = {c: all_columns[c] for c in all_columns if c in df.columns}
|
|
87
|
+
df = df.rename(columns=rename_dict)
|
|
88
|
+
|
|
89
|
+
# df["BDaysToExp"] = bd.count_bdays(df["TradeTimestamp"], df["ExpirationDate"])
|
|
90
|
+
|
|
91
|
+
# Remove percentage in all rate columns
|
|
92
|
+
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
93
|
+
df[rate_cols] = df[rate_cols] / 100
|
|
94
|
+
|
|
95
|
+
# Reorder columns based on the order of the dictionary
|
|
96
|
+
return df[rename_dict.values()]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def fetch_intraday(future_code: str) -> pd.DataFrame:
|
|
100
|
+
"""
|
|
101
|
+
Fetch the latest DI futures data from B3.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
pd.DataFrame: A Pandas pd.DataFrame containing the latest DI futures data.
|
|
105
|
+
"""
|
|
106
|
+
raw_df = _fetch_raw_df(future_code)
|
|
107
|
+
return _process_raw_df(raw_df)
|
|
@@ -3,7 +3,7 @@ import io
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
|
-
from . import
|
|
6
|
+
from .futures import historical as fh
|
|
7
7
|
|
|
8
8
|
# URL Constants
|
|
9
9
|
ANBIMA_NON_MEMBER_URL = "https://www.anbima.com.br/informacoes/merc-sec/arqs/"
|
|
@@ -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 =
|
|
153
|
+
df_di = fh.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)
|
|
@@ -23,16 +23,16 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Office/Business :: Financial :: Investment",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"requests",
|
|
27
26
|
"pandas>=2.0.0",
|
|
28
27
|
"numpy",
|
|
29
28
|
"beautifulsoup4",
|
|
30
29
|
"html5lib",
|
|
31
30
|
"lxml",
|
|
32
31
|
"python-calamine>=0.2.0",
|
|
32
|
+
"requests>=2.31.0",
|
|
33
33
|
]
|
|
34
34
|
dynamic = []
|
|
35
|
-
version = "0.7.
|
|
35
|
+
version = "0.7.8"
|
|
36
36
|
|
|
37
37
|
[project.license]
|
|
38
38
|
file = "LICENSE"
|
|
File without changes
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import pyield as yd
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def test_settlement_rate_with_old_holiday_list():
|
|
5
|
+
settlement_rates = {
|
|
6
|
+
"DI1N27": 0.09809,
|
|
7
|
+
"DI1F33": 0.10368,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
# 22-12-2023 is before the new holiday calendar
|
|
11
|
+
df = yd.fetch_asset(asset_code="DI1", reference_date="2023-12-22")
|
|
12
|
+
tickers = list(settlement_rates.keys()) # noqa: F841
|
|
13
|
+
result = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
|
|
14
|
+
assert result == list(settlement_rates.values())
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_settlement_rates_with_current_holiday_list():
|
|
18
|
+
settlement_rates = {
|
|
19
|
+
"DI1F24": 0.11644,
|
|
20
|
+
"DI1J24": 0.11300,
|
|
21
|
+
"DI1N24": 0.10786,
|
|
22
|
+
"DI1V24": 0.10321,
|
|
23
|
+
"DI1F25": 0.10031,
|
|
24
|
+
"DI1J25": 0.09852,
|
|
25
|
+
"DI1N25": 0.09715,
|
|
26
|
+
"DI1V25": 0.09651,
|
|
27
|
+
"DI1F26": 0.09583,
|
|
28
|
+
"DI1N26": 0.09631,
|
|
29
|
+
"DI1F27": 0.09683,
|
|
30
|
+
"DI1N27": 0.09794,
|
|
31
|
+
"DI1F29": 0.10042,
|
|
32
|
+
"DI1F31": 0.10240,
|
|
33
|
+
"DI1F33": 0.10331,
|
|
34
|
+
}
|
|
35
|
+
df = yd.fetch_asset(asset_code="DI1", reference_date="2023-12-26")
|
|
36
|
+
tickers = list(settlement_rates.keys()) # noqa: F841
|
|
37
|
+
results = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
|
|
38
|
+
assert results == list(settlement_rates.values())
|
pyield-0.7.7/pyield/__about__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.7.7"
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
from .common import get_expiration_date, get_old_expiration_date
|
|
2
|
-
from .ddi import fetch_past_ddi
|
|
3
|
-
from .di import fetch_last_di, fetch_past_di
|
|
4
|
-
from .frc import fetch_past_frc
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
"fetch_past_di",
|
|
8
|
-
"fetch_last_di",
|
|
9
|
-
"fetch_past_ddi",
|
|
10
|
-
"fetch_past_frc",
|
|
11
|
-
"get_expiration_date",
|
|
12
|
-
"get_old_expiration_date",
|
|
13
|
-
]
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
|
|
3
|
-
from pyield import futures as ft
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def test_valid_old_contract_code1():
|
|
7
|
-
expiration_code = "JAN3" # Valid contract code
|
|
8
|
-
trade_date = pd.Timestamp("2001-05-21")
|
|
9
|
-
result = ft.get_old_expiration_date(expiration_code, trade_date)
|
|
10
|
-
contract_expiration = pd.Timestamp("2003-01-02")
|
|
11
|
-
assert result == contract_expiration
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def test_valid_old_contract_code2():
|
|
15
|
-
expiration_code = "JAN3" # Valid contract code
|
|
16
|
-
trade_date = pd.Timestamp("1990-01-01")
|
|
17
|
-
result = ft.get_old_expiration_date(expiration_code, trade_date)
|
|
18
|
-
contract_expiration = pd.Timestamp("1993-01-04")
|
|
19
|
-
assert result == contract_expiration
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_invalid_old_contract_code():
|
|
23
|
-
expiration_code = "J3" # Invalid contract code
|
|
24
|
-
trade_date = pd.Timestamp("2001-01-02")
|
|
25
|
-
# Must return NaT
|
|
26
|
-
result = ft.get_old_expiration_date(expiration_code, trade_date)
|
|
27
|
-
assert pd.isnull(result)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_new_contract_code():
|
|
31
|
-
expiration_code = "F23" # Valid contract code
|
|
32
|
-
result = ft.get_expiration_date(expiration_code)
|
|
33
|
-
contract_expiration = pd.Timestamp("2023-01-02")
|
|
34
|
-
assert result == contract_expiration
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_settlement_rate_with_old_holiday_list():
|
|
38
|
-
settlement_rates = {
|
|
39
|
-
"DI1N27": 0.09809,
|
|
40
|
-
"DI1F33": 0.10368,
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
# 22-12-2023 is before the new holiday calendar
|
|
44
|
-
test_date = pd.Timestamp("2023-12-22")
|
|
45
|
-
df = ft.fetch_past_di(trade_date=test_date)
|
|
46
|
-
tickers = list(settlement_rates.keys()) # noqa: F841
|
|
47
|
-
result = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
|
|
48
|
-
assert result == list(settlement_rates.values())
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
def test_settlement_rates_with_current_holiday_list():
|
|
52
|
-
settlement_rates = {
|
|
53
|
-
"DI1F24": 0.11644,
|
|
54
|
-
"DI1J24": 0.11300,
|
|
55
|
-
"DI1N24": 0.10786,
|
|
56
|
-
"DI1V24": 0.10321,
|
|
57
|
-
"DI1F25": 0.10031,
|
|
58
|
-
"DI1J25": 0.09852,
|
|
59
|
-
"DI1N25": 0.09715,
|
|
60
|
-
"DI1V25": 0.09651,
|
|
61
|
-
"DI1F26": 0.09583,
|
|
62
|
-
"DI1N26": 0.09631,
|
|
63
|
-
"DI1F27": 0.09683,
|
|
64
|
-
"DI1N27": 0.09794,
|
|
65
|
-
"DI1F29": 0.10042,
|
|
66
|
-
"DI1F31": 0.10240,
|
|
67
|
-
"DI1F33": 0.10331,
|
|
68
|
-
}
|
|
69
|
-
test_date = pd.Timestamp("2023-12-26")
|
|
70
|
-
df = ft.fetch_past_di(trade_date=test_date)
|
|
71
|
-
tickers = list(settlement_rates.keys()) # noqa: F841
|
|
72
|
-
results = df.query("TickerSymbol in @tickers")["SettlementRate"].to_list()
|
|
73
|
-
assert results == list(settlement_rates.values())
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def test_non_business_day():
|
|
77
|
-
non_business_day = pd.Timestamp("2023-12-24")
|
|
78
|
-
# Test if it an empty DataFrame is returned
|
|
79
|
-
df = ft.fetch_past_di(trade_date=non_business_day)
|
|
80
|
-
assert df.empty
|
|
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
|