PYield 0.7.6__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.
Files changed (37) hide show
  1. {pyield-0.7.6 → pyield-0.7.8}/PKG-INFO +7 -7
  2. {pyield-0.7.6 → pyield-0.7.8}/README.md +5 -5
  3. pyield-0.7.8/pyield/__about__.py +1 -0
  4. {pyield-0.7.6 → pyield-0.7.8}/pyield/data_access.py +23 -15
  5. pyield-0.7.8/pyield/futures/historical/__init__.py +6 -0
  6. {pyield-0.7.6/pyield/futures → pyield-0.7.8/pyield/futures/historical}/common.py +95 -44
  7. pyield-0.7.8/pyield/futures/historical/dap.py +62 -0
  8. pyield-0.7.8/pyield/futures/historical/ddi.py +80 -0
  9. pyield-0.7.8/pyield/futures/historical/di.py +91 -0
  10. {pyield-0.7.6/pyield/futures → pyield-0.7.8/pyield/futures/historical}/di_xml.py +2 -2
  11. pyield-0.7.8/pyield/futures/historical/frc.py +60 -0
  12. pyield-0.7.8/pyield/futures/intraday/__init__.py +3 -0
  13. pyield-0.7.8/pyield/futures/intraday/trading_data.py +107 -0
  14. {pyield-0.7.6 → pyield-0.7.8}/pyield/projections.py +7 -4
  15. {pyield-0.7.6 → pyield-0.7.8}/pyield/treasuries.py +2 -2
  16. {pyield-0.7.6 → pyield-0.7.8}/pyproject.toml +2 -2
  17. pyield-0.7.8/tests/__init__.py +0 -0
  18. pyield-0.7.8/tests/test_futures.py +38 -0
  19. pyield-0.7.6/pyield/__about__.py +0 -1
  20. pyield-0.7.6/pyield/futures/__init__.py +0 -13
  21. pyield-0.7.6/pyield/futures/ddi.py +0 -145
  22. pyield-0.7.6/pyield/futures/di.py +0 -205
  23. pyield-0.7.6/pyield/futures/frc.py +0 -98
  24. pyield-0.7.6/tests/test_futures.py +0 -80
  25. {pyield-0.7.6 → pyield-0.7.8}/LICENSE +0 -0
  26. {pyield-0.7.6 → pyield-0.7.8}/pyield/__init__.py +0 -0
  27. {pyield-0.7.6 → pyield-0.7.8}/pyield/bday.py +0 -0
  28. {pyield-0.7.6 → pyield-0.7.8}/pyield/data_analysis.py +0 -0
  29. {pyield-0.7.6/tests → pyield-0.7.8/pyield/futures}/__init__.py +0 -0
  30. {pyield-0.7.6 → pyield-0.7.8}/pyield/holidays/__init__.py +0 -0
  31. {pyield-0.7.6 → pyield-0.7.8}/pyield/holidays/br_holidays_new.txt +0 -0
  32. {pyield-0.7.6 → pyield-0.7.8}/pyield/holidays/br_holidays_old.txt +0 -0
  33. {pyield-0.7.6 → pyield-0.7.8}/pyield/holidays/core.py +0 -0
  34. {pyield-0.7.6 → pyield-0.7.8}/pyield/indicators.py +0 -0
  35. {pyield-0.7.6 → pyield-0.7.8}/pyield/py.typed +0 -0
  36. {pyield-0.7.6 → pyield-0.7.8}/pyield/utils.py +0 -0
  37. {pyield-0.7.6 → 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.6
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
  [![PyPI version](https://img.shields.io/pypi/v/pyield.svg)](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 LastRate
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 LastRate
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
- elif asset_code.lower() in ["ltn", "lft", "ntn-f", "ntn-b"]:
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
- elif asset_code.lower() == "di1":
53
- today = pd.Timestamp.today().normalize()
54
- if normalized_date == today:
55
- return ft.fetch_last_di()
56
- else:
57
- return ft.fetch_past_di(trade_date=normalized_date, return_raw=return_raw)
58
- elif asset_code.lower() == "ddi":
59
- return ft.fetch_past_ddi(trade_date=normalized_date, return_raw=return_raw)
60
- elif asset_code.lower() == "frc":
61
- return ft.fetch_past_frc(trade_date=normalized_date, return_raw=return_raw)
62
- else:
63
- raise ValueError("Asset type not supported.")
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(
@@ -111,7 +119,7 @@ def fetch_indicator(
111
119
  raise ValueError("Indicator type not supported.")
112
120
 
113
121
 
114
- def fetch_projection(projection_code: str) -> dict:
122
+ def fetch_projection(projection_code: str) -> pr.IndicatorProjection:
115
123
  """
116
124
  Fetches a financial projection for a specified code and reference date.
117
125
 
@@ -0,0 +1,6 @@
1
+ from .dap import fetch_dap
2
+ from .ddi import fetch_ddi
3
+ from .di import fetch_di
4
+ from .frc import fetch_frc
5
+
6
+ __all__ = ["fetch_di", "fetch_ddi", "fetch_frc", "fetch_dap"]
@@ -3,7 +3,7 @@ import io
3
3
  import pandas as pd
4
4
  import requests
5
5
 
6
- from .. import bday
6
+ from ... import bday
7
7
 
8
8
 
9
9
  def get_expiration_date(expiration_code: str) -> pd.Timestamp:
@@ -131,7 +131,28 @@ def get_old_expiration_date(
131
131
  return pd.NaT # type: ignore
132
132
 
133
133
 
134
- def fetch_past_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
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:
135
156
  """
136
157
  Fetch the historical futures data from B3 for a specific trade date. If the data is
137
158
  not available, an empty DataFrame is returned.
@@ -177,54 +198,84 @@ def fetch_past_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame
177
198
  return df
178
199
 
179
200
 
180
- def fetch_last_raw_df(future_code: str) -> pd.DataFrame:
181
- """
182
- Fetch the latest data for a given future code from B3 derivatives quotation API.
183
-
184
- Args:
185
- future_code (str): The future code to fetch data for.
186
-
187
- Returns:
188
- pd.DataFrame: A DataFrame containing the normalized and cleaned data from the API.
189
-
190
- Raises:
191
- Exception: An exception is raised if the data fetch operation fails.
192
- """
193
-
194
- url = f"https://cotacao.b3.com.br/mds/api/v1/DerivativeQuotation/{future_code}"
201
+ def rename_columns(df: pd.DataFrame) -> pd.DataFrame:
202
+ all_columns = {
203
+ "VENCTO": "ExpirationCode",
204
+ "CONTR. ABERT.(1)": "OpenContracts", # At the start of the day
205
+ "CONTR. FECH.(2)": "OpenContractsEndSession", # At the end of the day
206
+ "NÚM. NEGOC.": "TradeCount",
207
+ "CONTR. NEGOC.": "TradeVolume",
208
+ "VOL.": "FinancialVolume",
209
+ "AJUSTE": "SettlementPrice",
210
+ "AJUSTE ANTER. (3)": "PrevSettlementRate",
211
+ "AJUSTE CORRIG. (4)": "AdjSettlementRate",
212
+ "AJUSTE DE REF.": "SettlementRate", # FRC
213
+ "PREÇO MÍN.": "MinRate",
214
+ "PREÇO MÉD.": "AvgRate",
215
+ "PREÇO MÁX.": "MaxRate",
216
+ "PREÇO ABERTU.": "FirstRate",
217
+ "ÚLT. PREÇO": "CloseRate",
218
+ "VAR. PTOS.": "PointsVariation",
219
+ # Attention: bid/ask rates are inverted
220
+ "ÚLT.OF. COMPRA": "CloseAskRate",
221
+ "ÚLT.OF. VENDA": "CloseBidRate",
222
+ }
223
+ rename_dict = {c: all_columns[c] for c in all_columns if c in df.columns}
224
+ return df.rename(columns=rename_dict)
195
225
 
196
- try:
197
- r = requests.get(url)
198
- r.raise_for_status() # Check for HTTP request errors
199
- except requests.exceptions.RequestException:
200
- raise Exception(f"Failed to fetch data for {future_code}.") from None
201
226
 
202
- r.encoding = "utf-8" # Explicitly set response encoding to utf-8 for consistency
227
+ def process_raw_df(
228
+ df: pd.DataFrame, trade_date: pd.Timestamp, asset_code: str
229
+ ) -> pd.DataFrame:
230
+ df = rename_columns(df)
203
231
 
204
- # Normalize JSON response into a flat table
205
- df = pd.json_normalize(r.json()["Scty"])
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]")
206
235
 
207
- # Clean and reformat the DataFrame columns
208
- df.columns = (df.columns
209
- .str.replace("SctyQtn.", "")
210
- .str.replace("asset.AsstSummry.", "")
211
- ) # fmt: skip
212
- df.drop(columns=["desc", "asset.code", "mkt.cd"], inplace=True)
236
+ df["TickerSymbol"] = asset_code + df["ExpirationCode"]
213
237
 
214
- # Convert maturity codes to datetime and drop rows with missing values
215
- df["mtrtyCode"] = pd.to_datetime(df["mtrtyCode"], errors="coerce")
216
- df.dropna(subset=["mtrtyCode"], inplace=True)
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)
217
245
 
218
- # Sort the DataFrame by maturity code and reset the index
219
- df.sort_values("mtrtyCode", inplace=True, ignore_index=True)
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)
220
252
 
221
- # Get current date and time
222
- now = pd.Timestamp.now().round("s")
223
- # Subtract 15 minutes from the current time to account for API delay
224
- trade_ts = now - pd.Timedelta(minutes=15)
225
- df["TradeTimestamp"] = trade_ts
253
+ return df
226
254
 
227
- # Convert DataFrame to use nullable data types for better type consistency
228
- df = df.convert_dtypes(dtype_backend="numpy_nullable")
229
255
 
230
- return df
256
+ def reorder_columns(df: pd.DataFrame):
257
+ all_columns = [
258
+ "TradeDate",
259
+ "TickerSymbol",
260
+ # "ExpirationCode",
261
+ "ExpirationDate",
262
+ "BDaysToExp",
263
+ "DaysToExp",
264
+ "OpenContracts",
265
+ # "OpenContractsEndSession" since there is no OpenContracts at the end of the
266
+ # day in XML data, it will be removed to avoid confusion with XML data
267
+ "TradeCount",
268
+ "TradeVolume",
269
+ "FinancialVolume",
270
+ "SettlementPrice",
271
+ "SettlementRate",
272
+ "FirstRate",
273
+ "MinRate",
274
+ "AvgRate",
275
+ "MaxRate",
276
+ "CloseAskRate",
277
+ "CloseBidRate",
278
+ "CloseRate",
279
+ ]
280
+ reordered_columns = [col for col in all_columns if col in df.columns]
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)
@@ -0,0 +1,80 @@
1
+ import pandas as pd
2
+
3
+ from . import common as cm
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
+ # Round to 5 (3 in %) dec. places (contract's current max. precision)
19
+ return rates.round(5)
20
+
21
+
22
+ def _process_ddi_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
23
+ """
24
+ Internal function to process and transform raw DI futures data.
25
+
26
+ Args:
27
+ df (pd.DataFrame): the raw DI DataFrame.
28
+ trade_date: a datetime-like object representing the trade date.
29
+
30
+ Returns:
31
+ pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
32
+ """
33
+ df["DaysToExp"] = (df["ExpirationDate"] - trade_date).dt.days
34
+ # Convert to nullable integer, since it is the default type in the library
35
+ df["DaysToExp"] = df["DaysToExp"].astype(pd.Int64Dtype())
36
+
37
+ # Remove expired contracts
38
+ df.query("DaysToExp > 0", inplace=True)
39
+
40
+ # Columns where 0 means NaN
41
+ rate_cols = [col for col in df.columns if "Rate" in col]
42
+ df[rate_cols] = df[rate_cols].div(100).round(5)
43
+
44
+ # Calculate SettlementRate
45
+ df["SettlementRate"] = _convert_prices_to_rates(
46
+ df["SettlementPrice"], df["DaysToExp"]
47
+ )
48
+
49
+ return df
50
+
51
+
52
+ def fetch_ddi(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
53
+ """
54
+ Fetchs the DDI futures data for a given date from B3.
55
+
56
+ This function fetches and processes the DI futures data from B3 for a specific
57
+ trade date. It's the primary external interface for accessing DI data.
58
+
59
+ Args:
60
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
61
+ raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
62
+ Defaults to False.
63
+
64
+ Returns:
65
+ pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
66
+
67
+ Examples:
68
+ >>> from pyield.futures import di
69
+ >>> di.fetch_ddi(pd.Timestamp("2021-01-04"))
70
+
71
+ Notes:
72
+ - DaysToExp: number of business days to ExpirationDate.
73
+ - OpenContracts: number of open contracts at the start of the trading day.
74
+ """
75
+ df_raw = cm.fetch_raw_df(asset_code="DDI", trade_date=trade_date)
76
+ if return_raw or df_raw.empty:
77
+ return df_raw
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)
@@ -0,0 +1,91 @@
1
+ import pandas as pd
2
+
3
+ from ... import bday as bd
4
+ from . import common as cm
5
+
6
+
7
+ def _convert_prices_to_rates(prices: pd.Series, bd: pd.Series) -> pd.Series:
8
+ """
9
+ Internal function to convert DI futures prices to rates.
10
+
11
+ Args:
12
+ prices (pd.Series): A pd.Series containing DI futures prices.
13
+ bd (pd.Series): A serie containing the number of business days to expiration.
14
+
15
+ Returns:
16
+ pd.Series: A pd.Series containing DI futures rates.
17
+ """
18
+ rates = (100_000 / prices) ** (252 / bd) - 1
19
+
20
+ # Round to 5 (3 in %) dec. places (contract's current max. precision)
21
+ return rates.round(5)
22
+
23
+
24
+ def _adjust_older_contracts_rates(df: pd.DataFrame, rate_cols: list) -> pd.DataFrame:
25
+ for col in rate_cols:
26
+ df[col] = _convert_prices_to_rates(df[col], df["BDaysToExp"])
27
+
28
+ # Invert low and high prices
29
+ df["MinRate"], df["MaxRate"] = df["MaxRate"], df["MinRate"]
30
+
31
+ return df
32
+
33
+
34
+ def _process_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
35
+ """
36
+ Internal function to process and transform raw DI futures data.
37
+
38
+ Args:
39
+ df (pd.DataFrame): the raw DI DataFrame.
40
+ trade_date: a datetime-like object representing the trade date.
41
+
42
+ Returns:
43
+ pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
44
+ """
45
+ df["BDaysToExp"] = bd.count_bdays(trade_date, df["ExpirationDate"])
46
+
47
+ # Remove expired contracts
48
+ df.query("BDaysToExp > 0", inplace=True)
49
+
50
+ rate_cols = [col for col in df.columns if "Rate" in col]
51
+ # Prior to 17/01/2002 (inclusive), prices were not converted to rates
52
+ if trade_date > pd.Timestamp("2002-01-17"):
53
+ # Remove % and round to 5 (3 in %) dec. places in rate columns
54
+ df[rate_cols] = df[rate_cols].div(100).round(5)
55
+ else:
56
+ df = _adjust_older_contracts_rates(df, rate_cols)
57
+
58
+ # Calculate SettlementRate
59
+ df["SettlementRate"] = _convert_prices_to_rates(
60
+ df["SettlementPrice"], df["BDaysToExp"]
61
+ )
62
+
63
+ return df
64
+
65
+
66
+ def fetch_di(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
67
+ """
68
+ Fetchs the DI futures data for a given date from B3.
69
+
70
+ This function fetches and processes the DI futures data from B3 for a specific
71
+ trade date. It's the primary external interface for accessing DI data. If the data
72
+ is not available, an empty DataFrame is returned.
73
+
74
+ Args:
75
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
76
+ return_raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
77
+ Defaults to False.
78
+
79
+ Returns:
80
+ pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
81
+
82
+ Notes:
83
+ - BDaysToExp: number of business days to ExpirationDate.
84
+ - OpenContracts: number of open contracts at the start of the trading day.
85
+ """
86
+ df_raw = cm.fetch_raw_df(asset_code="DI1", trade_date=trade_date)
87
+ if return_raw or df_raw.empty:
88
+ return df_raw
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 .. import bday
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": "LastRate",
209
+ "LastPric": "CloseRate",
210
210
  "AdjstdQtTax": "SettlementRate",
211
211
  # "RglrTxsQty"
212
212
  # "RglrTraddCtrcts"
@@ -0,0 +1,60 @@
1
+ import pandas as pd
2
+
3
+ from . import common as cm
4
+
5
+
6
+ def _process_frc_df(df: pd.DataFrame, trade_date: pd.Timestamp) -> pd.DataFrame:
7
+ """
8
+ Internal function to process and transform raw DI futures data.
9
+
10
+ Args:
11
+ df (pd.DataFrame): the raw DI DataFrame.
12
+ trade_date: a datetime-like object representing the trade date.
13
+
14
+ Returns:
15
+ pd.DataFrame: Processed and transformed data as a Pandas pd.DataFrame.
16
+ """
17
+ df = cm.rename_columns(df)
18
+
19
+ df["TradeDate"] = trade_date
20
+ # Convert to datetime64[ns] since it is pandas default type for timestamps
21
+ df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
22
+
23
+ # Contract code format was changed in 22/05/2006
24
+ if trade_date < pd.Timestamp("2006-05-22"):
25
+ df["ExpirationDate"] = df["ExpirationCode"].apply(
26
+ cm.get_old_expiration_date, args=(trade_date,)
27
+ )
28
+ else:
29
+ df["ExpirationDate"] = df["ExpirationCode"].apply(cm.get_expiration_date)
30
+
31
+ # Replace 0 values in rate columns with pd.NA and remove percentage
32
+ rate_cols = [col for col in df.columns if "Rate" in col]
33
+ # Round to 5 decimal places (3 in %) since it is the contract's precision
34
+ df[rate_cols] = df[rate_cols].replace(0, pd.NA).div(100).round(5)
35
+
36
+ return df
37
+
38
+
39
+ def fetch_frc(trade_date: pd.Timestamp, return_raw: bool = False) -> pd.DataFrame:
40
+ """
41
+ Fetchs the DI futures data for a given date from B3.
42
+
43
+ This function fetches and processes the DI futures data from B3 for a specific
44
+ trade date. It's the primary external interface for accessing DI data.
45
+
46
+ Args:
47
+ trade_date (pd.Timestamp): The trade date to fetch the DI futures data.
48
+ raw (bool): If True, returns the raw data as a Pandas pd.DataFrame.
49
+ Defaults to False.
50
+
51
+ Returns:
52
+ pd.DataFrame: A Pandas pd.DataFrame containing processed DI futures data.
53
+ """
54
+ df_raw = cm.fetch_raw_df(asset_code="FRC", trade_date=trade_date)
55
+ if return_raw or df_raw.empty:
56
+ return df_raw
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,3 @@
1
+ from .trading_data import fetch_intraday
2
+
3
+ __all__ = ["fetch_intraday"]