PYield 0.7.8__tar.gz → 0.7.9__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.8 → pyield-0.7.9}/PKG-INFO +1 -1
- pyield-0.7.9/pyield/__about__.py +1 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/__init__.py +6 -2
- {pyield-0.7.8 → pyield-0.7.9}/pyield/data_access.py +53 -27
- pyield-0.7.9/pyield/futures/__init__.py +4 -0
- pyield-0.7.8/pyield/futures/historical/common.py → pyield-0.7.9/pyield/futures/historical.py +90 -7
- pyield-0.7.8/pyield/futures/intraday/trading_data.py → pyield-0.7.9/pyield/futures/intraday.py +1 -1
- pyield-0.7.9/pyield/spreads.py +56 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/treasuries.py +0 -52
- {pyield-0.7.8 → pyield-0.7.9}/pyproject.toml +1 -1
- pyield-0.7.8/pyield/futures/historical/di_xml.py → pyield-0.7.9/tests/xml.py +3 -3
- pyield-0.7.8/pyield/__about__.py +0 -1
- pyield-0.7.8/pyield/data_analysis.py +0 -33
- pyield-0.7.8/pyield/futures/historical/__init__.py +0 -6
- pyield-0.7.8/pyield/futures/historical/dap.py +0 -62
- pyield-0.7.8/pyield/futures/historical/ddi.py +0 -80
- pyield-0.7.8/pyield/futures/historical/di.py +0 -91
- pyield-0.7.8/pyield/futures/historical/frc.py +0 -60
- pyield-0.7.8/pyield/futures/intraday/__init__.py +0 -3
- pyield-0.7.8/tests/__init__.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/LICENSE +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/README.md +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/bday.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/holidays/__init__.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/holidays/core.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/indicators.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/projections.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/py.typed +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/pyield/utils.py +0 -0
- {pyield-0.7.8/pyield/futures → pyield-0.7.9/tests}/__init__.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/tests/test_bday.py +0 -0
- {pyield-0.7.8 → pyield-0.7.9}/tests/test_futures.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.7.9"
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
from .__about__ import __version__
|
|
2
2
|
from .bday import count_bdays, generate_bdays, is_bday, offset_bdays
|
|
3
|
-
from .data_access import
|
|
4
|
-
|
|
3
|
+
from .data_access import (
|
|
4
|
+
calculate_spreads,
|
|
5
|
+
fetch_asset,
|
|
6
|
+
fetch_indicator,
|
|
7
|
+
fetch_projection,
|
|
8
|
+
)
|
|
5
9
|
|
|
6
10
|
__all__ = [
|
|
7
11
|
"__version__",
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
2
|
|
|
3
|
+
from . import futures as ft
|
|
3
4
|
from . import indicators as it
|
|
4
5
|
from . import projections as pr
|
|
6
|
+
from . import spreads as sp
|
|
5
7
|
from . import treasuries as tr
|
|
6
|
-
from .futures import historical as fh
|
|
7
|
-
from .futures import intraday as fi
|
|
8
8
|
from .utils import _normalize_date
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def fetch_asset(
|
|
12
|
-
asset_code: str,
|
|
13
|
-
reference_date: str | pd.Timestamp | None = None,
|
|
14
|
-
**kwargs,
|
|
12
|
+
asset_code: str, reference_date: str | pd.Timestamp | None = None
|
|
15
13
|
) -> pd.DataFrame:
|
|
16
14
|
"""
|
|
17
15
|
Fetches data for a specified asset based on type and reference date.
|
|
@@ -25,7 +23,11 @@ def fetch_asset(
|
|
|
25
23
|
- "DI1": One-day Interbank Deposit Futures (Futuro de DI) from B3.
|
|
26
24
|
- "DDI": DI x U.S. Dollar Spread Futures (Futuro de Cupom Cambial) from B3.
|
|
27
25
|
- "FRC": Forward Rate Agreement (FRA) from B3.
|
|
28
|
-
- "DAP": DI x IPCA Spread Futures
|
|
26
|
+
- "DAP": DI x IPCA Spread Futures.
|
|
27
|
+
- "DOL": U.S. Dollar Futures from B3.
|
|
28
|
+
- "WDO": Mini U.S. Dollar Futures from B3.
|
|
29
|
+
- "IND": Ibovespa Futures from B3.
|
|
30
|
+
- "WIN": Mini Ibovespa Futures from B3.
|
|
29
31
|
reference_date (str | pd.Timestamp | None): The reference date for which data is
|
|
30
32
|
fetched. Defaults to the last business day if None.
|
|
31
33
|
**kwargs: Additional keyword arguments, specifically:
|
|
@@ -42,31 +44,26 @@ def fetch_asset(
|
|
|
42
44
|
>>> fetch_asset('TRB', '2023-04-01')
|
|
43
45
|
>>> fetch_asset('DI1', '2023-04-01', return_raw=True)
|
|
44
46
|
"""
|
|
45
|
-
|
|
47
|
+
SUPPORTED_BONDS = ["LTN", "LFT", "NTN-F", "NTN-B"]
|
|
48
|
+
SUPPORTED_FUTURES = ["DI1", "DDI", "FRC", "DAP", "DOL", "WDO", "IND", "WIN"]
|
|
49
|
+
|
|
46
50
|
normalized_date = _normalize_date(reference_date)
|
|
47
51
|
|
|
48
52
|
today = pd.Timestamp.today().normalize()
|
|
49
53
|
if normalized_date == today:
|
|
50
|
-
return
|
|
54
|
+
return ft.fetch_intraday_df(future_code=asset_code.upper())
|
|
51
55
|
|
|
52
|
-
if asset_code.
|
|
53
|
-
return tr.fetch_bonds(reference_date=normalized_date
|
|
56
|
+
if asset_code.upper() == "TRB":
|
|
57
|
+
return tr.fetch_bonds(reference_date=normalized_date)
|
|
54
58
|
|
|
55
|
-
if asset_code.
|
|
59
|
+
if asset_code.upper() in SUPPORTED_BONDS:
|
|
56
60
|
df = tr.fetch_bonds(reference_date=normalized_date)
|
|
57
61
|
return df.query(f"BondType == '{asset_code.upper()}'")
|
|
58
62
|
|
|
59
|
-
if asset_code.
|
|
60
|
-
return
|
|
61
|
-
|
|
62
|
-
|
|
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)
|
|
63
|
+
if asset_code.upper() in SUPPORTED_FUTURES:
|
|
64
|
+
return ft.fetch_historical_df(
|
|
65
|
+
asset_code=asset_code.upper(), trade_date=normalized_date
|
|
66
|
+
)
|
|
70
67
|
|
|
71
68
|
raise ValueError("Asset type not supported.")
|
|
72
69
|
|
|
@@ -107,13 +104,13 @@ def fetch_indicator(
|
|
|
107
104
|
"""
|
|
108
105
|
normalized_date = _normalize_date(reference_date)
|
|
109
106
|
|
|
110
|
-
if indicator_code.
|
|
107
|
+
if indicator_code.upper() == "SELIC":
|
|
111
108
|
return it.fetch_selic_target(reference_date=normalized_date)
|
|
112
|
-
elif indicator_code.
|
|
109
|
+
elif indicator_code.upper() == "IPCA":
|
|
113
110
|
return it.fetch_ipca_mr(reference_date=normalized_date)
|
|
114
|
-
elif indicator_code.
|
|
111
|
+
elif indicator_code.upper() == "DI":
|
|
115
112
|
return it.fetch_di(reference_date=normalized_date)
|
|
116
|
-
elif indicator_code.
|
|
113
|
+
elif indicator_code.upper() == "VNA_LFT":
|
|
117
114
|
return it.fetch_vna_selic(reference_date=normalized_date)
|
|
118
115
|
else:
|
|
119
116
|
raise ValueError("Indicator type not supported.")
|
|
@@ -150,7 +147,36 @@ def fetch_projection(projection_code: str) -> pr.IndicatorProjection:
|
|
|
150
147
|
)
|
|
151
148
|
"""
|
|
152
149
|
|
|
153
|
-
if projection_code.
|
|
150
|
+
if projection_code.upper() == "IPCA_CM":
|
|
154
151
|
return pr.fetch_current_month_ipca_projection()
|
|
155
152
|
else:
|
|
156
153
|
raise ValueError("Projection type not supported.")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def calculate_spreads(
|
|
157
|
+
spread_type: str, reference_date: str | pd.Timestamp | None = None
|
|
158
|
+
) -> pd.DataFrame:
|
|
159
|
+
"""
|
|
160
|
+
Calculates spreads between assets based on the specified spread type.
|
|
161
|
+
If no reference date is provided, the function uses the previous business day.
|
|
162
|
+
|
|
163
|
+
Parameters:
|
|
164
|
+
spread_type (str): The type of spread to calculate. Available options are:
|
|
165
|
+
- "DI_vs_PRE": the spread between DI Futures and Treasury Pre-Fixed bonds.
|
|
166
|
+
reference_date (str | pd.Timestamp, optional): The reference date for the
|
|
167
|
+
spread calculation. If None or not provided, defaults to the previous
|
|
168
|
+
business day according to the Brazilian calendar.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
pd.DataFrame: A DataFrame containing the calculated spread in basis points.
|
|
172
|
+
The data is sorted by asset type and maturity/expiration date.
|
|
173
|
+
|
|
174
|
+
Raises:
|
|
175
|
+
ValueError: If an invalid spread type is provided.
|
|
176
|
+
"""
|
|
177
|
+
# Normalize the reference date
|
|
178
|
+
normalized_date = _normalize_date(reference_date)
|
|
179
|
+
if spread_type.upper() == "DI_VS_PRE":
|
|
180
|
+
return sp.calculate_di_spreads(normalized_date)
|
|
181
|
+
else:
|
|
182
|
+
raise ValueError("Invalid spread type.")
|
pyield-0.7.8/pyield/futures/historical/common.py → pyield-0.7.9/pyield/futures/historical.py
RENAMED
|
@@ -3,7 +3,18 @@ import io
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
|
-
from
|
|
6
|
+
from .. import bday
|
|
7
|
+
|
|
8
|
+
COUNT_CONVENTIONS = {
|
|
9
|
+
"DAP": 252,
|
|
10
|
+
"DI1": 252,
|
|
11
|
+
"DDI": 360,
|
|
12
|
+
"FRC": None,
|
|
13
|
+
"DOL": None,
|
|
14
|
+
"WDO": None,
|
|
15
|
+
"IND": None,
|
|
16
|
+
"WIN": None,
|
|
17
|
+
}
|
|
7
18
|
|
|
8
19
|
|
|
9
20
|
def get_expiration_date(expiration_code: str) -> pd.Timestamp:
|
|
@@ -141,12 +152,17 @@ def convert_prices_to_rates(
|
|
|
141
152
|
prices (pd.Series): The futures prices to be converted.
|
|
142
153
|
days_to_expiration (pd.Series): The number of days to expiration for each price.
|
|
143
154
|
count_convention (int): The count convention for the DI futures contract.
|
|
144
|
-
|
|
155
|
+
Can be 252 business days or 360 calendar days.
|
|
145
156
|
|
|
146
157
|
Returns:
|
|
147
158
|
pd.Series: A pd.Series containing the futures rates.
|
|
148
159
|
"""
|
|
149
|
-
|
|
160
|
+
if count_convention == 252:
|
|
161
|
+
rates = (100_000 / prices) ** (252 / days_to_expiration) - 1
|
|
162
|
+
elif count_convention == 360:
|
|
163
|
+
rates = (100_000 / prices - 1) * (360 / days_to_expiration)
|
|
164
|
+
else:
|
|
165
|
+
raise ValueError("Invalid count_convention. Must be 252 or 360.")
|
|
150
166
|
|
|
151
167
|
# Round to 5 (3 in %) dec. places (contract's current max. precision)
|
|
152
168
|
return rates.round(5)
|
|
@@ -198,6 +214,16 @@ def fetch_raw_df(asset_code: str, trade_date: pd.Timestamp) -> pd.DataFrame:
|
|
|
198
214
|
return df
|
|
199
215
|
|
|
200
216
|
|
|
217
|
+
def _adjust_older_contracts_rates(df: pd.DataFrame, rate_cols: list) -> pd.DataFrame:
|
|
218
|
+
for col in rate_cols:
|
|
219
|
+
df[col] = convert_prices_to_rates(df[col], df["BDaysToExp"], 252)
|
|
220
|
+
|
|
221
|
+
# Invert low and high prices
|
|
222
|
+
df["MinRate"], df["MaxRate"] = df["MaxRate"], df["MinRate"]
|
|
223
|
+
|
|
224
|
+
return df
|
|
225
|
+
|
|
226
|
+
|
|
201
227
|
def rename_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
202
228
|
all_columns = {
|
|
203
229
|
"VENCTO": "ExpirationCode",
|
|
@@ -224,11 +250,11 @@ def rename_columns(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
224
250
|
return df.rename(columns=rename_dict)
|
|
225
251
|
|
|
226
252
|
|
|
227
|
-
def
|
|
228
|
-
df: pd.DataFrame,
|
|
253
|
+
def process_df(
|
|
254
|
+
df: pd.DataFrame,
|
|
255
|
+
trade_date: pd.Timestamp,
|
|
256
|
+
asset_code: str,
|
|
229
257
|
) -> pd.DataFrame:
|
|
230
|
-
df = rename_columns(df)
|
|
231
|
-
|
|
232
258
|
df["TradeDate"] = trade_date
|
|
233
259
|
# Convert to datetime64[ns] since it is pandas default type for timestamps
|
|
234
260
|
df["TradeDate"] = df["TradeDate"].astype("datetime64[ns]")
|
|
@@ -243,6 +269,14 @@ def process_raw_df(
|
|
|
243
269
|
else:
|
|
244
270
|
df["ExpirationDate"] = df["ExpirationCode"].apply(get_expiration_date)
|
|
245
271
|
|
|
272
|
+
df["DaysToExp"] = (df["ExpirationDate"] - trade_date).dt.days
|
|
273
|
+
# Convert to nullable integer, since it is the default type in the library
|
|
274
|
+
df["DaysToExp"] = df["DaysToExp"].astype(pd.Int64Dtype())
|
|
275
|
+
# Remove expired contracts
|
|
276
|
+
df.query("DaysToExp > 0", inplace=True)
|
|
277
|
+
|
|
278
|
+
df["BDaysToExp"] = bday.count_bdays(trade_date, df["ExpirationDate"])
|
|
279
|
+
|
|
246
280
|
# Columns where 0 means NaN
|
|
247
281
|
cols_with_nan = [col for col in df.columns if "Rate" in col]
|
|
248
282
|
if "SettlementPrice" in df.columns:
|
|
@@ -250,6 +284,27 @@ def process_raw_df(
|
|
|
250
284
|
# Replace 0 with NaN in these columns
|
|
251
285
|
df[cols_with_nan] = df[cols_with_nan].replace(0, pd.NA)
|
|
252
286
|
|
|
287
|
+
rate_cols = [col for col in df.columns if "Rate" in col]
|
|
288
|
+
# Prior to 17/01/2002 (inclusive), DI prices were not converted to rates
|
|
289
|
+
if trade_date <= pd.Timestamp("2002-01-17") and asset_code == "DI1":
|
|
290
|
+
df = _adjust_older_contracts_rates(df, rate_cols)
|
|
291
|
+
else:
|
|
292
|
+
# Remove % and round to 5 (3 in %) dec. places in rate columns
|
|
293
|
+
df[rate_cols] = df[rate_cols].div(100).round(5)
|
|
294
|
+
|
|
295
|
+
if COUNT_CONVENTIONS[asset_code] == 252:
|
|
296
|
+
df["SettlementRate"] = convert_prices_to_rates(
|
|
297
|
+
prices=df["SettlementPrice"],
|
|
298
|
+
days_to_expiration=df["BDaysToExp"],
|
|
299
|
+
count_convention=252,
|
|
300
|
+
)
|
|
301
|
+
elif COUNT_CONVENTIONS[asset_code] == 360:
|
|
302
|
+
df["SettlementRate"] = convert_prices_to_rates(
|
|
303
|
+
prices=df["SettlementPrice"],
|
|
304
|
+
days_to_expiration=df["DaysToExp"],
|
|
305
|
+
count_convention=360,
|
|
306
|
+
)
|
|
307
|
+
|
|
253
308
|
return df
|
|
254
309
|
|
|
255
310
|
|
|
@@ -279,3 +334,31 @@ def reorder_columns(df: pd.DataFrame):
|
|
|
279
334
|
]
|
|
280
335
|
reordered_columns = [col for col in all_columns if col in df.columns]
|
|
281
336
|
return df[reordered_columns]
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def fetch_historical_df(
|
|
340
|
+
asset_code: str,
|
|
341
|
+
trade_date: pd.Timestamp,
|
|
342
|
+
) -> pd.DataFrame:
|
|
343
|
+
"""
|
|
344
|
+
Fetchs the futures data for a given date from B3.
|
|
345
|
+
|
|
346
|
+
This function fetches and processes the futures data from B3 for a specific
|
|
347
|
+
trade date. It's the primary external interface for accessing futures data.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
asset_code (str): The asset code to fetch the futures data.
|
|
351
|
+
trade_date (pd.Timestamp): The trade date to fetch the futures data.
|
|
352
|
+
count_convention (int): The count convention for the DI futures contract.
|
|
353
|
+
Can be 252 business days or 360 calendar days.
|
|
354
|
+
|
|
355
|
+
Returns:
|
|
356
|
+
pd.DataFrame: A Pandas pd.DataFrame containing processed futures data.
|
|
357
|
+
"""
|
|
358
|
+
df_raw = fetch_raw_df(asset_code=asset_code, trade_date=trade_date)
|
|
359
|
+
if df_raw.empty:
|
|
360
|
+
return df_raw
|
|
361
|
+
df = rename_columns(df_raw)
|
|
362
|
+
df = process_df(df, trade_date, asset_code)
|
|
363
|
+
df = reorder_columns(df)
|
|
364
|
+
return df
|
pyield-0.7.8/pyield/futures/intraday/trading_data.py → pyield-0.7.9/pyield/futures/intraday.py
RENAMED
|
@@ -96,7 +96,7 @@ def _process_raw_df(raw_df: pd.DataFrame) -> pd.DataFrame:
|
|
|
96
96
|
return df[rename_dict.values()]
|
|
97
97
|
|
|
98
98
|
|
|
99
|
-
def
|
|
99
|
+
def fetch_intraday_df(future_code: str) -> pd.DataFrame:
|
|
100
100
|
"""
|
|
101
101
|
Fetch the latest DI futures data from B3.
|
|
102
102
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from . import treasuries as tr
|
|
4
|
+
from .futures import historical as fh
|
|
5
|
+
|
|
6
|
+
# Constant for conversion to basis points
|
|
7
|
+
BPS_CONVERSION_FACTOR = 10_000
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def calculate_di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
11
|
+
"""
|
|
12
|
+
Calculates the DI spread for Brazilian treasury bonds (LTN and NTN-F) based on
|
|
13
|
+
ANBIMA's indicative rates.
|
|
14
|
+
|
|
15
|
+
This function fetches the indicative rates for Brazilian treasury securities (LTN
|
|
16
|
+
and NTN-F bonds) and the DI futures rates for a specified reference date,
|
|
17
|
+
calculating the spread between these rates in basis points. If no reference date is
|
|
18
|
+
provided, the function uses the previous business day.
|
|
19
|
+
|
|
20
|
+
Parameters:
|
|
21
|
+
reference_date (str | pd.Timestamp, optional): The reference date for the DI
|
|
22
|
+
spread calculation.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
pd.DataFrame: A DataFrame containing the bond type, reference date, maturity
|
|
26
|
+
date, and the calculated DI spread in basis points. The data is sorted by
|
|
27
|
+
bond type and maturity date.
|
|
28
|
+
"""
|
|
29
|
+
# Fetch DI rates for the reference date
|
|
30
|
+
df_di = fh.fetch_historical_df(asset_code="DI1", trade_date=reference_date)[
|
|
31
|
+
["ExpirationDate", "SettlementRate"]
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
# Renaming the columns to match the ANBIMA structure
|
|
35
|
+
df_di.rename(columns={"ExpirationDate": "MaturityDate"}, inplace=True)
|
|
36
|
+
|
|
37
|
+
# Adjusting maturity date to match bond data format
|
|
38
|
+
df_di["MaturityDate"] = df_di["MaturityDate"].dt.to_period("M").dt.to_timestamp()
|
|
39
|
+
|
|
40
|
+
# Fetch bond rates, filtering for LTN and NTN-F types
|
|
41
|
+
df_anbima = tr.fetch_bonds(reference_date, False)
|
|
42
|
+
df_anbima.query("BondType in ['LTN', 'NTN-F']", inplace=True)
|
|
43
|
+
|
|
44
|
+
# Merge bond and DI rates by maturity date to calculate spreads
|
|
45
|
+
df_final = pd.merge(df_anbima, df_di, how="left", on="MaturityDate")
|
|
46
|
+
|
|
47
|
+
# Calculate the DI spread as the difference between indicative and settlement rates
|
|
48
|
+
df_final["DISpread"] = df_final["IndicativeRate"] - df_final["SettlementRate"]
|
|
49
|
+
|
|
50
|
+
# Convert spread to basis points for clarity
|
|
51
|
+
df_final["DISpread"] = (BPS_CONVERSION_FACTOR * df_final["DISpread"]).round(2)
|
|
52
|
+
|
|
53
|
+
# Prepare and return the final sorted DataFrame
|
|
54
|
+
select_columns = ["BondType", "ReferenceDate", "MaturityDate", "DISpread"]
|
|
55
|
+
df_final = df_final[select_columns].copy()
|
|
56
|
+
return df_final.sort_values(["BondType", "MaturityDate"], ignore_index=True)
|
|
@@ -3,15 +3,10 @@ import io
|
|
|
3
3
|
import pandas as pd
|
|
4
4
|
import requests
|
|
5
5
|
|
|
6
|
-
from .futures import historical as fh
|
|
7
|
-
|
|
8
6
|
# URL Constants
|
|
9
7
|
ANBIMA_NON_MEMBER_URL = "https://www.anbima.com.br/informacoes/merc-sec/arqs/"
|
|
10
8
|
ANBIMA_MEMBER_URL = "http://www.anbima.associados.rtm/merc_sec/arqs/"
|
|
11
9
|
|
|
12
|
-
# Constant for conversion to basis points
|
|
13
|
-
BPS_CONVERSION_FACTOR = 10_000
|
|
14
|
-
|
|
15
10
|
|
|
16
11
|
def _get_anbima_content(reference_date: pd.Timestamp) -> str:
|
|
17
12
|
url_date = reference_date.strftime("%y%m%d")
|
|
@@ -128,50 +123,3 @@ def fetch_bonds(reference_date: pd.Timestamp, return_raw=False) -> pd.DataFrame:
|
|
|
128
123
|
df = _process_raw_df(df)
|
|
129
124
|
|
|
130
125
|
return df
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
def calculate_di_spreads(reference_date: pd.Timestamp) -> pd.DataFrame:
|
|
134
|
-
"""
|
|
135
|
-
Calculates the DI spread for Brazilian treasury bonds (LTN and NTN-F) based on
|
|
136
|
-
ANBIMA's indicative rates.
|
|
137
|
-
|
|
138
|
-
This function fetches the indicative rates for Brazilian treasury securities (LTN
|
|
139
|
-
and NTN-F bonds) and the DI futures rates for a specified reference date,
|
|
140
|
-
calculating the spread between these rates in basis points. If no reference date is
|
|
141
|
-
provided, the function uses the previous business day.
|
|
142
|
-
|
|
143
|
-
Parameters:
|
|
144
|
-
reference_date (str | pd.Timestamp, optional): The reference date for the DI
|
|
145
|
-
spread calculation.
|
|
146
|
-
|
|
147
|
-
Returns:
|
|
148
|
-
pd.DataFrame: A DataFrame containing the bond type, reference date, maturity
|
|
149
|
-
date, and the calculated DI spread in basis points. The data is sorted by
|
|
150
|
-
bond type and maturity date.
|
|
151
|
-
"""
|
|
152
|
-
# Fetch DI rates and adjust the maturity date format for compatibility
|
|
153
|
-
df_di = fh.fetch_di(reference_date)[["ExpirationDate", "SettlementRate"]]
|
|
154
|
-
|
|
155
|
-
# Renaming the columns to match the ANBIMA structure
|
|
156
|
-
df_di.rename(columns={"ExpirationDate": "MaturityDate"}, inplace=True)
|
|
157
|
-
|
|
158
|
-
# Adjusting maturity date to match bond data format
|
|
159
|
-
df_di["MaturityDate"] = df_di["MaturityDate"].dt.to_period("M").dt.to_timestamp()
|
|
160
|
-
|
|
161
|
-
# Fetch bond rates, filtering for LTN and NTN-F types
|
|
162
|
-
df_anbima = fetch_bonds(reference_date, False)
|
|
163
|
-
df_anbima.query("BondType in ['LTN', 'NTN-F']", inplace=True)
|
|
164
|
-
|
|
165
|
-
# Merge bond and DI rates by maturity date to calculate spreads
|
|
166
|
-
df_final = pd.merge(df_anbima, df_di, how="left", on="MaturityDate")
|
|
167
|
-
|
|
168
|
-
# Calculate the DI spread as the difference between indicative and settlement rates
|
|
169
|
-
df_final["DISpread"] = df_final["IndicativeRate"] - df_final["SettlementRate"]
|
|
170
|
-
|
|
171
|
-
# Convert spread to basis points for clarity
|
|
172
|
-
df_final["DISpread"] = (BPS_CONVERSION_FACTOR * df_final["DISpread"]).round(2)
|
|
173
|
-
|
|
174
|
-
# Prepare and return the final sorted DataFrame
|
|
175
|
-
select_columns = ["BondType", "ReferenceDate", "MaturityDate", "DISpread"]
|
|
176
|
-
df_final = df_final[select_columns].copy()
|
|
177
|
-
return df_final.sort_values(["BondType", "MaturityDate"], ignore_index=True)
|
|
@@ -7,8 +7,8 @@ import requests
|
|
|
7
7
|
from lxml import etree
|
|
8
8
|
from pandas import DataFrame, Timestamp
|
|
9
9
|
|
|
10
|
-
from
|
|
11
|
-
from . import
|
|
10
|
+
from ..pyield import bday
|
|
11
|
+
from ..pyield.futures import historical as ht
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
def _get_file_from_url(trade_date: Timestamp, source_type: str) -> io.BytesIO:
|
|
@@ -229,7 +229,7 @@ def _process_di_df(df_raw: DataFrame) -> DataFrame:
|
|
|
229
229
|
# Convert to datetime64[ns] since it is pandas default type for timestamps
|
|
230
230
|
df["TradDt"] = df["TradDt"].astype("datetime64[ns]")
|
|
231
231
|
|
|
232
|
-
expiration = df["TckrSymb"].str[3:].apply(
|
|
232
|
+
expiration = df["TckrSymb"].str[3:].apply(ht.get_expiration_date)
|
|
233
233
|
df.insert(2, "ExpirationDate", expiration)
|
|
234
234
|
|
|
235
235
|
business_days = bday.count_bdays(df["TradDt"], df["ExpirationDate"])
|
pyield-0.7.8/pyield/__about__.py
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.7.8"
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
|
|
3
|
-
from . import treasuries as tr
|
|
4
|
-
from .utils import _normalize_date
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def calculate_spreads(
|
|
8
|
-
spread_type: str, reference_date: str | pd.Timestamp | None = None
|
|
9
|
-
) -> pd.DataFrame:
|
|
10
|
-
"""
|
|
11
|
-
Calculates spreads between assets based on the specified spread type.
|
|
12
|
-
If no reference date is provided, the function uses the previous business day.
|
|
13
|
-
|
|
14
|
-
Parameters:
|
|
15
|
-
spread_type (str): The type of spread to calculate. Available options are:
|
|
16
|
-
- "DI_vs_PRE": the spread between DI Futures and Treasury Pre-Fixed bonds.
|
|
17
|
-
reference_date (str | pd.Timestamp, optional): The reference date for the
|
|
18
|
-
spread calculation. If None or not provided, defaults to the previous
|
|
19
|
-
business day according to the Brazilian calendar.
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
pd.DataFrame: A DataFrame containing the calculated spread in basis points.
|
|
23
|
-
The data is sorted by asset type and maturity/expiration date.
|
|
24
|
-
|
|
25
|
-
Raises:
|
|
26
|
-
ValueError: If an invalid spread type is provided.
|
|
27
|
-
"""
|
|
28
|
-
# Normalize the reference date
|
|
29
|
-
normalized_date = _normalize_date(reference_date)
|
|
30
|
-
if spread_type.lower() == "di_vs_pre":
|
|
31
|
-
return tr.calculate_di_spreads(normalized_date)
|
|
32
|
-
else:
|
|
33
|
-
raise ValueError("Invalid spread type.")
|
|
@@ -1,62 +0,0 @@
|
|
|
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)
|
|
@@ -1,80 +0,0 @@
|
|
|
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)
|
|
@@ -1,91 +0,0 @@
|
|
|
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)
|
|
@@ -1,60 +0,0 @@
|
|
|
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)
|
pyield-0.7.8/tests/__init__.py
DELETED
|
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
|
|
File without changes
|