PYield 0.19.2__tar.gz → 0.19.3__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.19.2 → pyield-0.19.3}/PKG-INFO +1 -1
- pyield-0.19.3/pyield/__about__.py +1 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bday.py +16 -56
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/bond_tools.py +1 -1
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/lft.py +2 -2
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ltn.py +4 -4
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ntnb.py +12 -12
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/ntnf.py +13 -13
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/anbima.py +3 -3
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/di.py +5 -5
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/__init__.py +1 -1
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/indicators.py +1 -1
- pyield-0.19.3/pyield/date_converter.py +82 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyproject.toml +1 -1
- pyield-0.19.2/pyield/__about__.py +0 -1
- pyield-0.19.2/pyield/date_converter.py +0 -65
- {pyield-0.19.2 → pyield-0.19.3}/LICENSE +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/README.md +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/__init__.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/bonds/__init__.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/calculators.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/__init__.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/data_cache.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/common.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/historical.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/intraday.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/futures_data/xml.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/data_sources/projections.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/__init__.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/br_holidays_new.txt +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/br_holidays_old.txt +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/holidays/holidays_manager.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/interpolator.py +0 -0
- {pyield-0.19.2 → pyield-0.19.3}/pyield/py.typed +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.19.3"
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
1
|
from typing import Literal, overload
|
|
3
2
|
|
|
4
3
|
import numpy as np
|
|
5
4
|
import pandas as pd
|
|
6
5
|
|
|
6
|
+
from . import date_converter as dc
|
|
7
7
|
from . import holidays
|
|
8
|
-
|
|
9
|
-
type ScalarDateTypes = str | np.datetime64 | pd.Timestamp | dt.datetime | dt.date
|
|
10
|
-
type PandasArrayDateTypes = pd.Series | pd.DatetimeIndex | pd.Index
|
|
11
|
-
type ArrayDateTypes = PandasArrayDateTypes | np.ndarray | list | tuple
|
|
8
|
+
from .date_converter import ArrayDateTypes, ScalarDateTypes
|
|
12
9
|
|
|
13
10
|
type ArrayIntTypes = np.ndarray | pd.Series | list | tuple
|
|
14
11
|
type ScalarIntTypes = int | np.integer
|
|
@@ -17,44 +14,6 @@ type ScalarIntTypes = int | np.integer
|
|
|
17
14
|
br_holidays = holidays.BrHolidays()
|
|
18
15
|
|
|
19
16
|
|
|
20
|
-
@overload
|
|
21
|
-
def _convert_input_dates(dates: ScalarDateTypes) -> pd.Timestamp: ...
|
|
22
|
-
@overload
|
|
23
|
-
def _convert_input_dates(dates: ArrayDateTypes) -> pd.Series: ...
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _convert_input_dates(
|
|
27
|
-
dates: ScalarDateTypes | ArrayDateTypes,
|
|
28
|
-
) -> pd.Timestamp | pd.Series:
|
|
29
|
-
result = pd.to_datetime(dates, dayfirst=True)
|
|
30
|
-
if isinstance(result, (pd.Timestamp, dt.date, dt.datetime, np.datetime64)):
|
|
31
|
-
result = pd.Timestamp(result).normalize()
|
|
32
|
-
elif isinstance(result, (pd.Series, pd.DatetimeIndex)):
|
|
33
|
-
result = pd.Series(result).astype("datetime64[ns]")
|
|
34
|
-
else:
|
|
35
|
-
raise ValueError("Invalid date input type.")
|
|
36
|
-
|
|
37
|
-
return result
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
def _convert_to_numpy_date(
|
|
41
|
-
dates: pd.Timestamp | pd.Series,
|
|
42
|
-
) -> np.datetime64 | np.ndarray:
|
|
43
|
-
"""
|
|
44
|
-
Converts the input dates to a numpy datetime64[D] format.
|
|
45
|
-
|
|
46
|
-
Args:
|
|
47
|
-
dates (Timestamp | Series): A single date or a Series of dates.
|
|
48
|
-
|
|
49
|
-
Returns:
|
|
50
|
-
np.datetime64 | np.ndarray: The input dates in a numpy datetime64[D] format.
|
|
51
|
-
"""
|
|
52
|
-
if isinstance(dates, pd.Timestamp):
|
|
53
|
-
return np.datetime64(dates, "D")
|
|
54
|
-
else:
|
|
55
|
-
return dates.to_numpy().astype("datetime64[D]")
|
|
56
|
-
|
|
57
|
-
|
|
58
17
|
@overload
|
|
59
18
|
def offset(
|
|
60
19
|
dates: ScalarDateTypes,
|
|
@@ -188,14 +147,14 @@ def offset(
|
|
|
188
147
|
information on error handling and behavior, refer to the `numpy.busday_offset`
|
|
189
148
|
documentation: https://numpy.org/doc/stable/reference/generated/numpy.busday_offset.html
|
|
190
149
|
"""
|
|
191
|
-
|
|
150
|
+
converted_dates = dc.convert_input_dates(dates)
|
|
192
151
|
|
|
193
152
|
selected_holidays = br_holidays.get_applicable_holidays(
|
|
194
|
-
|
|
153
|
+
converted_dates, holiday_list
|
|
195
154
|
)
|
|
196
|
-
selected_holidays_np =
|
|
155
|
+
selected_holidays_np = dc.convert_to_numpy_date(selected_holidays)
|
|
197
156
|
|
|
198
|
-
dates_np =
|
|
157
|
+
dates_np = dc.convert_to_numpy_date(converted_dates)
|
|
199
158
|
offsetted_dates_np = np.busday_offset(
|
|
200
159
|
dates_np, offsets=offset, roll=roll, holidays=selected_holidays_np
|
|
201
160
|
)
|
|
@@ -304,18 +263,18 @@ def count(
|
|
|
304
263
|
1 19
|
|
305
264
|
dtype: Int64
|
|
306
265
|
"""
|
|
307
|
-
|
|
308
|
-
|
|
266
|
+
converted_start = dc.convert_input_dates(start)
|
|
267
|
+
converted_end = dc.convert_input_dates(end)
|
|
309
268
|
|
|
310
269
|
# Determine which list of holidays to use
|
|
311
270
|
selected_holidays = br_holidays.get_applicable_holidays(
|
|
312
|
-
|
|
271
|
+
converted_start, holiday_list
|
|
313
272
|
)
|
|
314
|
-
selected_holidays_np =
|
|
273
|
+
selected_holidays_np = dc.convert_to_numpy_date(selected_holidays)
|
|
315
274
|
|
|
316
275
|
# Convert inputs to numpy datetime64[D] before calling numpy.busday_count
|
|
317
|
-
start_np =
|
|
318
|
-
end_np =
|
|
276
|
+
start_np = dc.convert_to_numpy_date(converted_start)
|
|
277
|
+
end_np = dc.convert_to_numpy_date(converted_end)
|
|
319
278
|
|
|
320
279
|
result_np = np.busday_count(start_np, end_np, holidays=selected_holidays_np)
|
|
321
280
|
if isinstance(result_np, np.integer):
|
|
@@ -371,11 +330,12 @@ def generate(
|
|
|
371
330
|
https://pandas.pydata.org/docs/reference/api/pandas.bdate_range.html.
|
|
372
331
|
"""
|
|
373
332
|
if start:
|
|
374
|
-
converted_start =
|
|
333
|
+
converted_start = dc.convert_input_dates(start)
|
|
375
334
|
else:
|
|
376
335
|
converted_start = pd.Timestamp.today()
|
|
336
|
+
|
|
377
337
|
if end:
|
|
378
|
-
converted_end =
|
|
338
|
+
converted_end = dc.convert_input_dates(end)
|
|
379
339
|
else:
|
|
380
340
|
converted_end = pd.Timestamp.today()
|
|
381
341
|
|
|
@@ -410,6 +370,6 @@ def is_business_day(date: ScalarDateTypes) -> bool:
|
|
|
410
370
|
>>> bday.is_business_day("25-12-2023") # Christmas
|
|
411
371
|
False
|
|
412
372
|
"""
|
|
413
|
-
converted_date =
|
|
373
|
+
converted_date = dc.convert_input_dates(date)
|
|
414
374
|
shifted_date = offset(converted_date, 0) # Shift the date if it is not a bus. day
|
|
415
375
|
return converted_date == shifted_date
|
|
@@ -77,7 +77,7 @@ def di_spreads(reference_date: str | pd.Timestamp) -> pd.DataFrame:
|
|
|
77
77
|
bond type and maturity date.
|
|
78
78
|
"""
|
|
79
79
|
# Fetch DI rates for the reference date
|
|
80
|
-
reference_date = dc.
|
|
80
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
81
81
|
df_di = di.data(reference_date, adj_expirations=True)
|
|
82
82
|
if "SettlementRate" not in df_di.columns:
|
|
83
83
|
raise ValueError("DI rates data is missing the 'SettlementRate' column.")
|
|
@@ -62,8 +62,8 @@ def quotation(
|
|
|
62
62
|
98.9645
|
|
63
63
|
"""
|
|
64
64
|
# Validate and normalize dates
|
|
65
|
-
settlement = dc.
|
|
66
|
-
maturity = dc.
|
|
65
|
+
settlement = dc.convert_input_dates(settlement)
|
|
66
|
+
maturity = dc.convert_input_dates(maturity)
|
|
67
67
|
|
|
68
68
|
# The number of bdays between settlement (inclusive) and the maturity (exclusive)
|
|
69
69
|
bdays = bday.count(settlement, maturity)
|
|
@@ -66,8 +66,8 @@ def price(
|
|
|
66
66
|
"""
|
|
67
67
|
|
|
68
68
|
# Validate and normalize dates
|
|
69
|
-
settlement = dc.
|
|
70
|
-
maturity = dc.
|
|
69
|
+
settlement = dc.convert_input_dates(settlement)
|
|
70
|
+
maturity = dc.convert_input_dates(maturity)
|
|
71
71
|
|
|
72
72
|
# Calculate the number of business days between settlement and cash flow dates
|
|
73
73
|
bdays = bday.count(settlement, maturity)
|
|
@@ -138,8 +138,8 @@ def historical_premium(
|
|
|
138
138
|
If the data is not available, returns NaN.
|
|
139
139
|
"""
|
|
140
140
|
# Convert input dates to a consistent format
|
|
141
|
-
reference_date = dc.
|
|
142
|
-
maturity = dc.
|
|
141
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
142
|
+
maturity = dc.convert_input_dates(maturity)
|
|
143
143
|
|
|
144
144
|
# Retrieve LTN rates for the reference date
|
|
145
145
|
df_anbima = rates(reference_date)
|
|
@@ -99,8 +99,8 @@ def _generate_coupon_dates_map(
|
|
|
99
99
|
pd.Series: Series of coupon dates within the specified range.
|
|
100
100
|
"""
|
|
101
101
|
# Validate and normalize dates
|
|
102
|
-
start = dc.
|
|
103
|
-
end = dc.
|
|
102
|
+
start = dc.convert_input_dates(start)
|
|
103
|
+
end = dc.convert_input_dates(end)
|
|
104
104
|
|
|
105
105
|
# Initialize the first coupon date based on the reference date
|
|
106
106
|
reference_year = start.year
|
|
@@ -138,8 +138,8 @@ def payment_dates(
|
|
|
138
138
|
pd.Series: Series of coupon dates within the specified range.
|
|
139
139
|
"""
|
|
140
140
|
# Validate and normalize dates
|
|
141
|
-
settlement = dc.
|
|
142
|
-
maturity = dc.
|
|
141
|
+
settlement = dc.convert_input_dates(settlement)
|
|
142
|
+
maturity = dc.convert_input_dates(maturity)
|
|
143
143
|
_check_maturities(maturity)
|
|
144
144
|
|
|
145
145
|
# Check if maturity date is after the start date
|
|
@@ -180,8 +180,8 @@ def cash_flows(
|
|
|
180
180
|
- CashFlow: Cash flow value for the bond
|
|
181
181
|
"""
|
|
182
182
|
# Validate and normalize dates
|
|
183
|
-
settlement = dc.
|
|
184
|
-
maturity = dc.
|
|
183
|
+
settlement = dc.convert_input_dates(settlement)
|
|
184
|
+
maturity = dc.convert_input_dates(maturity)
|
|
185
185
|
|
|
186
186
|
# Get the coupon dates between the settlement and maturity dates
|
|
187
187
|
p_dates = payment_dates(settlement, maturity)
|
|
@@ -225,8 +225,8 @@ def quotation(
|
|
|
225
225
|
99.5341
|
|
226
226
|
"""
|
|
227
227
|
# Validate and normalize dates
|
|
228
|
-
settlement = dc.
|
|
229
|
-
maturity = dc.
|
|
228
|
+
settlement = dc.convert_input_dates(settlement)
|
|
229
|
+
maturity = dc.convert_input_dates(maturity)
|
|
230
230
|
_check_maturities(maturity)
|
|
231
231
|
|
|
232
232
|
cf_df = cash_flows(settlement, maturity)
|
|
@@ -306,7 +306,7 @@ def spot_rates(
|
|
|
306
306
|
- SpotRate: The real spot rate for the bond.
|
|
307
307
|
"""
|
|
308
308
|
# Process and validate the input data
|
|
309
|
-
settlement = dc.
|
|
309
|
+
settlement = dc.convert_input_dates(settlement)
|
|
310
310
|
maturities = pd.to_datetime(maturities, errors="raise", dayfirst=True)
|
|
311
311
|
_check_maturities(maturities)
|
|
312
312
|
|
|
@@ -401,7 +401,7 @@ def bei_rates(
|
|
|
401
401
|
settlement date to the bond's maturity.
|
|
402
402
|
"""
|
|
403
403
|
# Normalize input dates
|
|
404
|
-
settlement = dc.
|
|
404
|
+
settlement = dc.convert_input_dates(settlement)
|
|
405
405
|
ntnb_maturities = pd.to_datetime(ntnb_maturities, errors="coerce", dayfirst=True)
|
|
406
406
|
|
|
407
407
|
# Calculate Real Interest Rate (RIR)
|
|
@@ -434,8 +434,8 @@ def duration(
|
|
|
434
434
|
maturity: str | pd.Timestamp,
|
|
435
435
|
rate: float,
|
|
436
436
|
) -> float:
|
|
437
|
-
settlement = dc.
|
|
438
|
-
maturity = dc.
|
|
437
|
+
settlement = dc.convert_input_dates(settlement)
|
|
438
|
+
maturity = dc.convert_input_dates(maturity)
|
|
439
439
|
|
|
440
440
|
df = cash_flows(settlement, maturity)
|
|
441
441
|
df["BY"] = bday.count(settlement, df["PaymentDate"]) / 252
|
|
@@ -85,8 +85,8 @@ def coupon_dates(
|
|
|
85
85
|
(exclusive) and maturity (inclusive) dates.
|
|
86
86
|
"""
|
|
87
87
|
# Validate and normalize dates
|
|
88
|
-
settlement = dc.
|
|
89
|
-
maturity = dc.
|
|
88
|
+
settlement = dc.convert_input_dates(settlement)
|
|
89
|
+
maturity = dc.convert_input_dates(maturity)
|
|
90
90
|
|
|
91
91
|
# Check if the maturity date is valid
|
|
92
92
|
_check_maturity_date(maturity)
|
|
@@ -131,8 +131,8 @@ def cash_flows(
|
|
|
131
131
|
pd.DataFrame: DataFrame with columns "PaymentDate" and "CashFlow".
|
|
132
132
|
"""
|
|
133
133
|
# Validate input dates
|
|
134
|
-
settlement = dc.
|
|
135
|
-
maturity = dc.
|
|
134
|
+
settlement = dc.convert_input_dates(settlement)
|
|
135
|
+
maturity = dc.convert_input_dates(maturity)
|
|
136
136
|
_check_maturity_date(maturity)
|
|
137
137
|
|
|
138
138
|
# Get the coupon payment dates between the settlement and maturity dates
|
|
@@ -221,7 +221,7 @@ def spot_rates( # noqa
|
|
|
221
221
|
pd.DataFrame: DataFrame with columns "MaturityDate" and "SpotRate"
|
|
222
222
|
"""
|
|
223
223
|
# Process and validate the input data
|
|
224
|
-
settlement = dc.
|
|
224
|
+
settlement = dc.convert_input_dates(settlement)
|
|
225
225
|
|
|
226
226
|
# Create flat forward interpolators for LTN and NTN-F rates
|
|
227
227
|
ltn_rate_interpolator = it.Interpolator(
|
|
@@ -379,8 +379,8 @@ def di_net_spread( # noqa
|
|
|
379
379
|
float: The net DI spread in basis points.
|
|
380
380
|
"""
|
|
381
381
|
# Create an interpolator for the DI rates using the flat-forward method
|
|
382
|
-
settlement = dc.
|
|
383
|
-
ntnf_maturity = dc.
|
|
382
|
+
settlement = dc.convert_input_dates(settlement)
|
|
383
|
+
ntnf_maturity = dc.convert_input_dates(ntnf_maturity)
|
|
384
384
|
|
|
385
385
|
ff_interpolator = it.Interpolator(
|
|
386
386
|
"flat_forward",
|
|
@@ -420,8 +420,8 @@ def premium(
|
|
|
420
420
|
di_expirations: pd.Series,
|
|
421
421
|
di_rates: pd.Series,
|
|
422
422
|
) -> float:
|
|
423
|
-
ntnf_maturity = dc.
|
|
424
|
-
settlement = dc.
|
|
423
|
+
ntnf_maturity = dc.convert_input_dates(ntnf_maturity)
|
|
424
|
+
settlement = dc.convert_input_dates(settlement)
|
|
425
425
|
|
|
426
426
|
df = cash_flows(settlement, ntnf_maturity, adj_payment_dates=True)
|
|
427
427
|
df["BDays"] = bday.count(settlement, df["PaymentDate"])
|
|
@@ -460,8 +460,8 @@ def historical_premium(
|
|
|
460
460
|
reference_date: str | pd.Timestamp,
|
|
461
461
|
maturity: str | pd.Timestamp, # noqa
|
|
462
462
|
) -> float:
|
|
463
|
-
reference_date = dc.
|
|
464
|
-
maturity = dc.
|
|
463
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
464
|
+
maturity = dc.convert_input_dates(maturity)
|
|
465
465
|
|
|
466
466
|
df_ntnf = rates(reference_date)
|
|
467
467
|
if df_ntnf.empty:
|
|
@@ -503,8 +503,8 @@ def duration(
|
|
|
503
503
|
maturity: str | pd.Timestamp,
|
|
504
504
|
rate: float,
|
|
505
505
|
) -> float:
|
|
506
|
-
settlement = dc.
|
|
507
|
-
maturity = dc.
|
|
506
|
+
settlement = dc.convert_input_dates(settlement)
|
|
507
|
+
maturity = dc.convert_input_dates(maturity)
|
|
508
508
|
|
|
509
509
|
df = cash_flows(settlement, maturity)
|
|
510
510
|
df["BY"] = bday.count(settlement, df["PaymentDate"]) / 252
|
|
@@ -110,7 +110,7 @@ def data(
|
|
|
110
110
|
bond_type: str | None = None,
|
|
111
111
|
) -> pd.DataFrame:
|
|
112
112
|
# Normalize the reference date
|
|
113
|
-
reference_date = dc.
|
|
113
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
114
114
|
file_content = _get_file_content(reference_date)
|
|
115
115
|
|
|
116
116
|
if not file_content:
|
|
@@ -134,7 +134,7 @@ def rates(
|
|
|
134
134
|
) -> pd.DataFrame:
|
|
135
135
|
df = get_anbima_dataframe()
|
|
136
136
|
|
|
137
|
-
reference_date = dc.
|
|
137
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
138
138
|
df.query("ReferenceDate == @reference_date", inplace=True)
|
|
139
139
|
|
|
140
140
|
if df.empty:
|
|
@@ -159,7 +159,7 @@ def pre_maturities(
|
|
|
159
159
|
reference_date: str | pd.Timestamp,
|
|
160
160
|
) -> pd.Series:
|
|
161
161
|
df = get_anbima_dataframe()
|
|
162
|
-
reference_date = dc.
|
|
162
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
163
163
|
df.query("ReferenceDate == @reference_date", inplace=True)
|
|
164
164
|
df.query("BondType in ['LTN', 'NTN-F']", inplace=True)
|
|
165
165
|
maturity_dates = df["MaturityDate"].drop_duplicates()
|
|
@@ -30,7 +30,7 @@ def data(
|
|
|
30
30
|
expiration dates and selected rates. The DataFrame is sorted by the
|
|
31
31
|
expiration date.
|
|
32
32
|
"""
|
|
33
|
-
trade_date = dc.
|
|
33
|
+
trade_date = dc.convert_input_dates(trade_date)
|
|
34
34
|
df = get_di_dataframe()
|
|
35
35
|
df.query("TradeDate == @trade_date", inplace=True)
|
|
36
36
|
|
|
@@ -86,7 +86,7 @@ def expirations(
|
|
|
86
86
|
Returns:
|
|
87
87
|
pd.Series: A Series of unique expiration dates for DI contracts.
|
|
88
88
|
"""
|
|
89
|
-
trade_date = dc.
|
|
89
|
+
trade_date = dc.convert_input_dates(trade_date)
|
|
90
90
|
df = data(trade_date, adj_expirations, prefixed_filter)
|
|
91
91
|
df = df.drop_duplicates(subset=["ExpirationDate"], ignore_index=True)
|
|
92
92
|
return df["ExpirationDate"]
|
|
@@ -118,7 +118,7 @@ def rate(
|
|
|
118
118
|
if the rate cannot be determined.
|
|
119
119
|
"""
|
|
120
120
|
# Convert input dates to consistent format
|
|
121
|
-
trade_date = dc.
|
|
121
|
+
trade_date = dc.convert_input_dates(trade_date)
|
|
122
122
|
# Adjust expiration date to the nearest business day
|
|
123
123
|
expiration = bday.offset(expiration, 0)
|
|
124
124
|
|
|
@@ -180,10 +180,10 @@ def trade_dates(
|
|
|
180
180
|
"""
|
|
181
181
|
df = get_di_dataframe()
|
|
182
182
|
if start_date:
|
|
183
|
-
start_date = dc.
|
|
183
|
+
start_date = dc.convert_input_dates(start_date)
|
|
184
184
|
df = df.query("TradeDate >= @start_date")
|
|
185
185
|
if end_date:
|
|
186
|
-
end_date = dc.
|
|
186
|
+
end_date = dc.convert_input_dates(end_date)
|
|
187
187
|
df = df.query("TradeDate <= @end_date")
|
|
188
188
|
|
|
189
189
|
df = df.drop_duplicates(subset=["TradeDate"], ignore_index=True)
|
|
@@ -47,7 +47,7 @@ def futures(
|
|
|
47
47
|
if contract_code not in SUPPORTED_FUTURES:
|
|
48
48
|
raise ValueError("Futures contract not supported.")
|
|
49
49
|
|
|
50
|
-
trade_date = dc.
|
|
50
|
+
trade_date = dc.convert_input_dates(trade_date)
|
|
51
51
|
|
|
52
52
|
# First, try to fetch historical data for the specified date
|
|
53
53
|
df = fetch_historical_df(contract_code, trade_date)
|
|
@@ -13,7 +13,7 @@ def indicator(
|
|
|
13
13
|
indicator_type: Literal["IPCA_MR", "SELIC_TARGET", "SELIC_OVER", "DI", "VNA_LFT"],
|
|
14
14
|
reference_date: str | pd.Timestamp,
|
|
15
15
|
) -> float:
|
|
16
|
-
reference_date = dc.
|
|
16
|
+
reference_date = dc.convert_input_dates(reference_date)
|
|
17
17
|
ind_type = str(indicator_type).upper()
|
|
18
18
|
if ind_type == "IPCA_MR":
|
|
19
19
|
return ipca_monthly_rate(reference_date)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import datetime as dt
|
|
2
|
+
import re
|
|
3
|
+
from typing import overload
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
|
|
8
|
+
type ScalarDateTypes = str | np.datetime64 | pd.Timestamp | dt.datetime | dt.date
|
|
9
|
+
type PandasArrayDateTypes = pd.Series | pd.DatetimeIndex | pd.Index
|
|
10
|
+
type ArrayDateTypes = PandasArrayDateTypes | np.ndarray | list | tuple
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def validate_year_format(value):
|
|
14
|
+
"""If the input date is a string, it validates the year format."""
|
|
15
|
+
if not isinstance(value, str):
|
|
16
|
+
return
|
|
17
|
+
|
|
18
|
+
value = value.strip().lower()
|
|
19
|
+
if value in {"today", "now"}:
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
if re.match(r"^\d{4}-", value):
|
|
23
|
+
error_msg = f"Invalid format: {value}. Day first format is required. Example: '31-05-2024'." # noqa
|
|
24
|
+
raise ValueError(error_msg)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def check_first_value(dates):
|
|
28
|
+
match dates:
|
|
29
|
+
case None:
|
|
30
|
+
raise ValueError("Date cannot be None.")
|
|
31
|
+
case str():
|
|
32
|
+
validate_year_format(dates)
|
|
33
|
+
case list() | tuple() | np.ndarray() | pd.Series():
|
|
34
|
+
validate_year_format(dates[0])
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@overload
|
|
38
|
+
def convert_input_dates(dates: ScalarDateTypes) -> pd.Timestamp: ...
|
|
39
|
+
@overload
|
|
40
|
+
def convert_input_dates(dates: ArrayDateTypes) -> pd.Series: ...
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def convert_input_dates(
|
|
44
|
+
dates: ScalarDateTypes | ArrayDateTypes,
|
|
45
|
+
) -> pd.Timestamp | pd.Series:
|
|
46
|
+
check_first_value(dates)
|
|
47
|
+
|
|
48
|
+
result = pd.to_datetime(dates, dayfirst=True)
|
|
49
|
+
|
|
50
|
+
match result:
|
|
51
|
+
case pd.Timestamp():
|
|
52
|
+
result = result.normalize()
|
|
53
|
+
case dt.datetime() | dt.date() | np.datetime64():
|
|
54
|
+
result = pd.Timestamp(result).normalize()
|
|
55
|
+
case pd.Series():
|
|
56
|
+
result = result.astype("datetime64[ns]")
|
|
57
|
+
case pd.DatetimeIndex():
|
|
58
|
+
result = pd.Series(result).astype("datetime64[ns]")
|
|
59
|
+
case None:
|
|
60
|
+
raise ValueError("Date cannot be None.")
|
|
61
|
+
case _:
|
|
62
|
+
raise ValueError("Invalid date input type.")
|
|
63
|
+
|
|
64
|
+
return result
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def convert_to_numpy_date(
|
|
68
|
+
dates: pd.Timestamp | pd.Series,
|
|
69
|
+
) -> np.datetime64 | np.ndarray:
|
|
70
|
+
"""
|
|
71
|
+
Converts the input dates to a numpy datetime64[D] format.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
dates (Timestamp | Series): A single date or a Series of dates.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
np.datetime64 | np.ndarray: The input dates in a numpy datetime64[D] format.
|
|
78
|
+
"""
|
|
79
|
+
if isinstance(dates, pd.Timestamp):
|
|
80
|
+
return np.datetime64(dates, "D")
|
|
81
|
+
else:
|
|
82
|
+
return dates.to_numpy().astype("datetime64[D]")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.19.2"
|
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
|
-
import re
|
|
3
|
-
|
|
4
|
-
import numpy as np
|
|
5
|
-
import pandas as pd
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _starts_with_year(date_str: str) -> bool:
|
|
9
|
-
"""Check if the date string starts with a four-digit year."""
|
|
10
|
-
if date_str in {"today", "now"}:
|
|
11
|
-
return False
|
|
12
|
-
else:
|
|
13
|
-
return bool(re.match(r"^\d{4}-", date_str))
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def convert_date(
|
|
17
|
-
input_date: str | pd.Timestamp | np.datetime64 | dt.date | dt.datetime,
|
|
18
|
-
) -> pd.Timestamp:
|
|
19
|
-
"""
|
|
20
|
-
Convert a date to pandas Timestamp adjusted to midnight.
|
|
21
|
-
|
|
22
|
-
Args:
|
|
23
|
-
reference_date (str | pd.Timestamp | np.datetime64): The date to convert.
|
|
24
|
-
If str, it should be with day first format (e.g. "31-05-2024").
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
pd.Timestamp: A normalized pandas Timestamp.
|
|
28
|
-
|
|
29
|
-
Raises:
|
|
30
|
-
ValueError: If the input date format is invalid.
|
|
31
|
-
|
|
32
|
-
Notes:
|
|
33
|
-
- Normalization means setting the time component of the timestamp to midnight.
|
|
34
|
-
- Business day calculations consider Brazilian holidays.
|
|
35
|
-
|
|
36
|
-
Examples:
|
|
37
|
-
>>> convert_date("31-05-2024")
|
|
38
|
-
Timestamp('2024-05-31 00:00:00')
|
|
39
|
-
"""
|
|
40
|
-
match input_date:
|
|
41
|
-
case None:
|
|
42
|
-
raise ValueError("Date cannot be None.")
|
|
43
|
-
case str():
|
|
44
|
-
if _starts_with_year(input_date):
|
|
45
|
-
raise ValueError(
|
|
46
|
-
f"Invalid date format: {input_date}. Day first format is required (e.g. '31-05-2024')." # noqa
|
|
47
|
-
)
|
|
48
|
-
# Parse the date string with day first format
|
|
49
|
-
output_date = pd.to_datetime(input_date, dayfirst=True)
|
|
50
|
-
case pd.Timestamp():
|
|
51
|
-
# Use Timestamp as is
|
|
52
|
-
output_date = input_date
|
|
53
|
-
case np.datetime64():
|
|
54
|
-
# Convert numpy datetime to pandas Timestamp
|
|
55
|
-
output_date = pd.Timestamp(input_date)
|
|
56
|
-
case dt.date():
|
|
57
|
-
# Convert datetime.date to pandas Timestamp
|
|
58
|
-
output_date = pd.Timestamp(input_date)
|
|
59
|
-
case dt.datetime():
|
|
60
|
-
# Convert datetime.datetime to pandas Timestamp
|
|
61
|
-
output_date = pd.Timestamp(input_date)
|
|
62
|
-
case _:
|
|
63
|
-
raise ValueError(f"Date format not recognized: {input_date}")
|
|
64
|
-
# Normalize the final date to midnight
|
|
65
|
-
return output_date.normalize()
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|