Module scrilla.util.dater
Expand source code
import datetime
from datetime import date
import math
import holidays
from typing import Any, List, Tuple, Union
import dateutil.easter as easter
from scrilla.settings import DATE_FORMAT
def today() -> datetime.date:
"""
Returns today's date
"""
return datetime.date.today()
def validate_order_of_dates(start_date: date, end_date: date) -> Tuple[date, date]:
"""
Returns the inputted dates as an tuple ordered from earliest to latest.
"""
delta = (end_date - start_date).days
if delta < 0:
return end_date, start_date
return start_date, end_date
def parse(date_string: str) -> Union[date, None]:
"""
Converts a date string in the 'YYYY-MM-DD' format to a Python `datetime.date`.
"""
return datetime.datetime.strptime(date_string, DATE_FORMAT).date()
def validate_date(this_date: Any) -> date:
if isinstance(this_date, str):
return parse(this_date)
if not isinstance(this_date, date):
raise ValueError(
f'{this_date} is neither date nor \'{DATE_FORMAT}\' formatted string')
return this_date
def validate_date_range(start_date: Any, end_date: Any) -> Tuple[date, date]:
if isinstance(start_date, str):
start_date = parse(start_date)
elif not isinstance(start_date, date):
raise ValueError(
f'{start_date} is neither date nor \'{DATE_FORMAT}\' formatted string')
if isinstance(end_date, str):
end_date = parse(end_date)
elif not isinstance(end_date, date):
raise ValueError(
f'{end_date} is neither date nor \'{DATE_FORMAT}\' formatted string')
return validate_order_of_dates(start_date, end_date)
def validate_date_list(dates: Union[List[Union[datetime.date, str]]]) -> Union[List[datetime.date], None]:
"""
Raises
------
1. **ValueError**
If the supplied list of dates contains unexpected data types, this error will be thrown.
"""
verified_dates = []
for this_date in dates:
if isinstance(this_date, str):
verified_dates.append(parse(this_date))
continue
if isinstance(this_date, datetime.date):
verified_dates.append(this_date)
continue
raise ValueError(
f'{this_date} is neither date nor \'{DATE_FORMAT}\'formatted string')
return verified_dates
def to_string(this_date: Union[date, None] = None) -> str:
"""
Returns a datetime formatted as 'YYYY-MM-DD'. If no date is provided, function will return today's formatted date.
"""
if this_date is None:
return to_string(today())
return datetime.datetime.strftime(this_date, DATE_FORMAT)
def is_date_today(this_date: Union[date, str]) -> bool:
return (validate_date(this_date) == datetime.date.today())
def is_future_date(this_date: Union[date, str]) -> bool:
return (validate_date(this_date) - today()).days > 0
def truncate_future_from_date(this_date: Union[date, str]) -> datetime.date:
this_date = validate_date(this_date)
if is_future_date(this_date):
return today()
return this_date
def last_close_date():
right_now = datetime.datetime.now()
trading_close_today = right_now.replace(hour=16)
if right_now > trading_close_today:
return right_now.date()
return get_previous_business_date(right_now.date())
def is_date_weekend(this_date: Union[date, str]) -> bool:
return validate_date(this_date).weekday() in [5, 6]
def is_date_holiday(this_date: Union[date, str]) -> bool:
this_date = validate_date(this_date)
us_holidays = holidays.UnitedStates(years=this_date.year)
# generate list without columbus day and veterans day since markets are open on those days
trading_holidays = [
"Columbus Day", "Columbus Day (Observed)", "Veterans Day", "Veterans Day (Observed)"]
# markets are open
# see here: https://www.barrons.com/articles/stock-market-open-close-new-years-eve-monday-hours-51640891577
if datetime.datetime(year=this_date.year+1, month=1, day=1).weekday() in [5, 6]:
trading_holidays += ["New Year's Day (Observed)"]
custom_holidays = [that_date for that_date in list(
us_holidays) if us_holidays[that_date] not in trading_holidays]
# add good friday to list since markets are closed on good friday
custom_holidays.append(easter.easter(
year=this_date.year) - datetime.timedelta(days=2))
return (this_date in custom_holidays)
def get_last_trading_date() -> date:
"""
Returns
-------
The last full trading day. If today is a trading day and the time is past market close, today's date will be returned. Otherwise, the previous business day's date will be returned.
"""
todays_date = datetime.datetime.now()
if is_date_holiday(todays_date) or is_date_weekend(todays_date):
return get_previous_business_date(todays_date.date())
return last_close_date()
def this_date_or_last_trading_date(this_date: Union[date, str, None] = None) -> date:
if this_date is None:
return get_last_trading_date()
this_date = validate_date(this_date)
if is_date_holiday(this_date) or is_date_weekend(this_date):
return get_previous_business_date(this_date)
if is_date_today(this_date):
return last_close_date()
return this_date
def format_date_range(start_date: date, end_date: date) -> str:
result = ""
if start_date is not None:
start_string = to_string(start_date)
result += f'From {start_string}'
if end_date is not None:
end_string = to_string(end_date)
result += f' Until {end_string}'
return result
def is_trading_date(this_date: Union[date, str]) -> bool:
this_date = validate_date(this_date)
return not is_date_weekend(this_date) and not is_date_holiday(this_date)
def intersect_with_trading_dates(date_key_dict: dict) -> dict:
return {this_date: date_key_dict[this_date] for this_date in date_key_dict if is_trading_date(this_date)}
def get_holidays_between(start_date: Union[date, str], end_date: Union[date, str]) -> int:
if isinstance(start_date, date):
start_date = to_string(start_date)
if isinstance(end_date, date):
end_date = to_string(end_date)
us_holidays = holidays.UnitedStates()
return len(us_holidays[start_date: end_date])
# YYYY-MM-DD
def consecutive_trading_days(start_date: Union[date, str], end_date: Union[date, str]) -> bool:
"""
Parameters
----------
1. **start_date_string**: ``str``
The start date of the time period under consideration. Must be formatted "YYYY-MM-DD"
2. **end_date_string**: ``str``
The end date of the time period under consideration. Must be formatted "YYYY-MM-DD"
Returns
-------
True
if start_date_string and end_date_string are consecutive trading days, i.e. Tuesday -> Wednesday or Friday -> Monday,
or Tuesday -> Thursday where Wednesday is a Holiday.
False
if start_date_string and end_date_string are NOT consecutive trading days.
"""
start_date, end_date = validate_date_range(start_date, end_date)
if is_date_weekend(start_date) or is_date_weekend(end_date):
return False
delta = (end_date - start_date).days
if delta < 0:
start_date, end_date = end_date, start_date
delta = end_date - start_date
holiday_count = get_holidays_between(
start_date=start_date, end_date=end_date)
if (delta - holiday_count) == 0:
return False
if (delta - holiday_count) == 1:
return True
if ((delta - holiday_count) > 1 and (delta - holiday_count) < 4):
start_week, end_week = start_date.isocalendar()[
1], end_date.isocalendar()[1]
if start_week == end_week:
return False
return True
return False
def dates_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[date]:
"""
Returns a list of dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes `start_date` and `end_date`.
Parameters
----------
1. **start_date**: ``datetime.date``
Start date of the date range.
2. **end_date**: ``datetime.date``
End date of the date range.
"""
start_date, end_date = validate_date_range(start_date, end_date)
return [start_date + datetime.timedelta(x) for x in range((end_date - start_date).days+1)]
def days_between(start_date: Union[date, str], end_date: Union[date, str]) -> int:
start_date, end_date = validate_date_range(start_date, end_date)
return int((end_date - start_date).days) + 1
# excludes start_date
def business_dates_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[date]:
"""
Returns a list of business dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes `start_date` and `dates`
Parameters
----------
1. **start_date**: ``datetime.date``
Start date of the date range.
2. **end_date**: ``datetime.date``
End date of the date range.
"""
start_date, end_date = validate_date_range(start_date, end_date)
dates = []
for x in range((end_date - start_date).days+1):
this_date = start_date + datetime.timedelta(x)
if is_trading_date(this_date):
dates.append(this_date)
return dates
def business_days_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[int]:
start_date, end_date = validate_date_range(start_date, end_date)
dates = dates_between(start_date, end_date)
return len([1 for this_date in dates if is_trading_date(this_date)])
def weekends_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[int]:
start_date, end_date = validate_date_range(start_date, end_date)
dates = dates_between(start_date, end_date)
return len([1 for day in dates if day.weekday() > 4])
def decrement_date_by_days(start_date: Union[date, str], days: int):
start_date = validate_date(start_date)
while days > 0:
days -= 1
start_date -= datetime.timedelta(days=1)
return start_date
def decrement_date_by_business_days(start_date: Union[date, str], business_days: int) -> date:
"""
Subtracts `business_days`, ignoring weekends and trading holidays, from `start_date`
"""
start_date = validate_date(start_date)
first_pass = True
while business_days > 0:
if is_trading_date(start_date):
if first_pass:
first_pass = False
else:
business_days -= 1
if business_days > 0:
start_date -= datetime.timedelta(days=1)
return start_date
def increment_date_by_business_days(start_date: Union[date, str], business_days: int) -> date:
start_date = validate_date(start_date)
while business_days > 0:
if is_trading_date(start_date):
business_days -= 1
start_date += datetime.timedelta(days=1)
return start_date
def get_next_business_date(this_date: Union[date, str]) -> date:
this_date = validate_date(this_date)
while not is_trading_date(this_date):
this_date += datetime.timedelta(days=1)
return this_date
def get_previous_business_date(this_date: Union[date, str]) -> date:
this_date = decrement_date_by_days(start_date=this_date, days=1)
while not is_trading_date(this_date):
this_date -= datetime.timedelta(days=1)
return this_date
# in years
def get_time_to_next_month(todays_date: date = today(), trading_days: int = 252) -> float:
"""
Parameters
----------
1. **todays_date**: ``date``
*Optional*. Reference date for calculation.
2. **trading_days**: ``int``
*Optional*. Number of trading days in a year. Defaults to 252.
"""
# TODO: what if first day of the month falls on non-trading days?
todays_date = datetime.date.today()
next_month = datetime.date(
year=todays_date.year, month=(todays_date.month+1), day=1)
return ((next_month - todays_date).days / trading_days)
def get_time_to_next_year(todays_date: date = today(), trading_days: int = 252) -> float:
"""
Parameters
----------
1. **todays_date**: ``date``
*Optional*. Reference date for calculation.
2. **trading_days**: ``int``
*Optional*. Number of trading days in a year. Defaults to 252.
"""
# TODO: what if first day of year falls on non-trading day?
# which it will, by definition. fuckwit.
next_year = datetime.datetime(year=todays_date.year+1, day=1, month=1)
return ((next_year - todays_date).days / trading_days)
def get_time_to_next_quarter(todays_date: date = today(), trading_days: int = 252) -> float:
"""
Parameters
----------
1. **todays_date**: ``date``
*Optional*. Reference date for calculation.
2. **trading_days**: ``int``
*Optional*. Number of trading days in a year. Defaults to 252.
"""
# TODO: what if first day of quarter falls on non-trading days?
first_q = datetime.date(year=todays_date.year, month=1, day=1)
second_q = datetime.date(year=todays_date.year, month=4, day=1)
third_q = datetime.date(year=todays_date.year, month=7, day=1)
fourth_q = datetime.date(year=todays_date.year, month=10, day=1)
next_first_q = datetime.date(year=(todays_date.year+1), month=1, day=1)
first_delta = (first_q - todays_date).days / trading_days
second_delta = (second_q - todays_date).days / trading_days
third_delta = (third_q - todays_date).days / trading_days
fourth_delta = (fourth_q - todays_date).days / trading_days
next_delta = (next_first_q - todays_date).days / trading_days
return min(i for i in [first_delta, second_delta, third_delta, fourth_delta, next_delta] if i > 0)
def get_time_to_next_period(starting_date: Union[date, str], period: float) -> float:
"""
Divides the year into segments of equal length 'period' and then calculates the time from today until
the next period.
Parameters
----------
1. **starting_date**: ``Union[date, str]``
Starting day of the period. Not to be confused with today. This is the point in time when the recurring event started.
2. **period**: float
Length of one period, measured in years.
"""
if period is None:
return 0
starting_date = validate_date(starting_date)
todays_date = datetime.date.today()
floored_days = math.floor(365*period)
while ((starting_date - todays_date).days < 0):
starting_date += datetime.timedelta(days=floored_days)
return float((todays_date - starting_date).days / 365)
Functions
def business_dates_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> List[datetime.date]
-
Returns a list of business dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes
start_date
anddates
Parameters
- start_date:
datetime.date
Start date of the date range. - end_date:
datetime.date
End date of the date range.
Expand source code
def business_dates_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[date]: """ Returns a list of business dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes `start_date` and `dates` Parameters ---------- 1. **start_date**: ``datetime.date`` Start date of the date range. 2. **end_date**: ``datetime.date`` End date of the date range. """ start_date, end_date = validate_date_range(start_date, end_date) dates = [] for x in range((end_date - start_date).days+1): this_date = start_date + datetime.timedelta(x) if is_trading_date(this_date): dates.append(this_date) return dates
- start_date:
def business_days_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> List[int]
-
Expand source code
def business_days_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[int]: start_date, end_date = validate_date_range(start_date, end_date) dates = dates_between(start_date, end_date) return len([1 for this_date in dates if is_trading_date(this_date)])
def consecutive_trading_days(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> bool
-
Parameters
- start_date_string:
str
The start date of the time period under consideration. Must be formatted "YYYY-MM-DD" - end_date_string:
str
The end date of the time period under consideration. Must be formatted "YYYY-MM-DD"
Returns
True if start_date_string and end_date_string are consecutive trading days, i.e. Tuesday -> Wednesday or Friday -> Monday, or Tuesday -> Thursday where Wednesday is a Holiday. False if start_date_string and end_date_string are NOT consecutive trading days.
Expand source code
def consecutive_trading_days(start_date: Union[date, str], end_date: Union[date, str]) -> bool: """ Parameters ---------- 1. **start_date_string**: ``str`` The start date of the time period under consideration. Must be formatted "YYYY-MM-DD" 2. **end_date_string**: ``str`` The end date of the time period under consideration. Must be formatted "YYYY-MM-DD" Returns ------- True if start_date_string and end_date_string are consecutive trading days, i.e. Tuesday -> Wednesday or Friday -> Monday, or Tuesday -> Thursday where Wednesday is a Holiday. False if start_date_string and end_date_string are NOT consecutive trading days. """ start_date, end_date = validate_date_range(start_date, end_date) if is_date_weekend(start_date) or is_date_weekend(end_date): return False delta = (end_date - start_date).days if delta < 0: start_date, end_date = end_date, start_date delta = end_date - start_date holiday_count = get_holidays_between( start_date=start_date, end_date=end_date) if (delta - holiday_count) == 0: return False if (delta - holiday_count) == 1: return True if ((delta - holiday_count) > 1 and (delta - holiday_count) < 4): start_week, end_week = start_date.isocalendar()[ 1], end_date.isocalendar()[1] if start_week == end_week: return False return True return False
- start_date_string:
def dates_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> List[datetime.date]
-
Returns a list of dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes
start_date
andend_date
.Parameters
- start_date:
datetime.date
Start date of the date range. - end_date:
datetime.date
End date of the date range.
Expand source code
def dates_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[date]: """ Returns a list of dates between the inputted dates. "Between" is used in the inclusive sense, i.e. the list includes `start_date` and `end_date`. Parameters ---------- 1. **start_date**: ``datetime.date`` Start date of the date range. 2. **end_date**: ``datetime.date`` End date of the date range. """ start_date, end_date = validate_date_range(start_date, end_date) return [start_date + datetime.timedelta(x) for x in range((end_date - start_date).days+1)]
- start_date:
def days_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> int
-
Expand source code
def days_between(start_date: Union[date, str], end_date: Union[date, str]) -> int: start_date, end_date = validate_date_range(start_date, end_date) return int((end_date - start_date).days) + 1
def decrement_date_by_business_days(start_date: Union[datetime.date, str], business_days: int) ‑> datetime.date
-
Subtracts
business_days
, ignoring weekends and trading holidays, fromstart_date
Expand source code
def decrement_date_by_business_days(start_date: Union[date, str], business_days: int) -> date: """ Subtracts `business_days`, ignoring weekends and trading holidays, from `start_date` """ start_date = validate_date(start_date) first_pass = True while business_days > 0: if is_trading_date(start_date): if first_pass: first_pass = False else: business_days -= 1 if business_days > 0: start_date -= datetime.timedelta(days=1) return start_date
def decrement_date_by_days(start_date: Union[datetime.date, str], days: int)
-
Expand source code
def decrement_date_by_days(start_date: Union[date, str], days: int): start_date = validate_date(start_date) while days > 0: days -= 1 start_date -= datetime.timedelta(days=1) return start_date
def format_date_range(start_date: datetime.date, end_date: datetime.date) ‑> str
-
Expand source code
def format_date_range(start_date: date, end_date: date) -> str: result = "" if start_date is not None: start_string = to_string(start_date) result += f'From {start_string}' if end_date is not None: end_string = to_string(end_date) result += f' Until {end_string}' return result
def get_holidays_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> int
-
Expand source code
def get_holidays_between(start_date: Union[date, str], end_date: Union[date, str]) -> int: if isinstance(start_date, date): start_date = to_string(start_date) if isinstance(end_date, date): end_date = to_string(end_date) us_holidays = holidays.UnitedStates() return len(us_holidays[start_date: end_date])
def get_last_trading_date() ‑> datetime.date
-
Returns
The last full trading day. If today is a trading day and the time is past market close, today's date will be returned. Otherwise, the previous business day's date will be returned.
Expand source code
def get_last_trading_date() -> date: """ Returns ------- The last full trading day. If today is a trading day and the time is past market close, today's date will be returned. Otherwise, the previous business day's date will be returned. """ todays_date = datetime.datetime.now() if is_date_holiday(todays_date) or is_date_weekend(todays_date): return get_previous_business_date(todays_date.date()) return last_close_date()
def get_next_business_date(this_date: Union[datetime.date, str]) ‑> datetime.date
-
Expand source code
def get_next_business_date(this_date: Union[date, str]) -> date: this_date = validate_date(this_date) while not is_trading_date(this_date): this_date += datetime.timedelta(days=1) return this_date
def get_previous_business_date(this_date: Union[datetime.date, str]) ‑> datetime.date
-
Expand source code
def get_previous_business_date(this_date: Union[date, str]) -> date: this_date = decrement_date_by_days(start_date=this_date, days=1) while not is_trading_date(this_date): this_date -= datetime.timedelta(days=1) return this_date
def get_time_to_next_month(todays_date: datetime.date = datetime.date(2022, 4, 26), trading_days: int = 252) ‑> float
-
Parameters
- todays_date:
date
Optional. Reference date for calculation. - trading_days:
int
Optional. Number of trading days in a year. Defaults to 252.
Expand source code
def get_time_to_next_month(todays_date: date = today(), trading_days: int = 252) -> float: """ Parameters ---------- 1. **todays_date**: ``date`` *Optional*. Reference date for calculation. 2. **trading_days**: ``int`` *Optional*. Number of trading days in a year. Defaults to 252. """ # TODO: what if first day of the month falls on non-trading days? todays_date = datetime.date.today() next_month = datetime.date( year=todays_date.year, month=(todays_date.month+1), day=1) return ((next_month - todays_date).days / trading_days)
- todays_date:
def get_time_to_next_period(starting_date: Union[datetime.date, str], period: float) ‑> float
-
Divides the year into segments of equal length 'period' and then calculates the time from today until the next period.
Parameters
- starting_date:
Union[date, str]
Starting day of the period. Not to be confused with today. This is the point in time when the recurring event started. - period: float Length of one period, measured in years.
Expand source code
def get_time_to_next_period(starting_date: Union[date, str], period: float) -> float: """ Divides the year into segments of equal length 'period' and then calculates the time from today until the next period. Parameters ---------- 1. **starting_date**: ``Union[date, str]`` Starting day of the period. Not to be confused with today. This is the point in time when the recurring event started. 2. **period**: float Length of one period, measured in years. """ if period is None: return 0 starting_date = validate_date(starting_date) todays_date = datetime.date.today() floored_days = math.floor(365*period) while ((starting_date - todays_date).days < 0): starting_date += datetime.timedelta(days=floored_days) return float((todays_date - starting_date).days / 365)
- starting_date:
def get_time_to_next_quarter(todays_date: datetime.date = datetime.date(2022, 4, 26), trading_days: int = 252) ‑> float
-
Parameters
- todays_date:
date
Optional. Reference date for calculation. - trading_days:
int
Optional. Number of trading days in a year. Defaults to 252.
Expand source code
def get_time_to_next_quarter(todays_date: date = today(), trading_days: int = 252) -> float: """ Parameters ---------- 1. **todays_date**: ``date`` *Optional*. Reference date for calculation. 2. **trading_days**: ``int`` *Optional*. Number of trading days in a year. Defaults to 252. """ # TODO: what if first day of quarter falls on non-trading days? first_q = datetime.date(year=todays_date.year, month=1, day=1) second_q = datetime.date(year=todays_date.year, month=4, day=1) third_q = datetime.date(year=todays_date.year, month=7, day=1) fourth_q = datetime.date(year=todays_date.year, month=10, day=1) next_first_q = datetime.date(year=(todays_date.year+1), month=1, day=1) first_delta = (first_q - todays_date).days / trading_days second_delta = (second_q - todays_date).days / trading_days third_delta = (third_q - todays_date).days / trading_days fourth_delta = (fourth_q - todays_date).days / trading_days next_delta = (next_first_q - todays_date).days / trading_days return min(i for i in [first_delta, second_delta, third_delta, fourth_delta, next_delta] if i > 0)
- todays_date:
def get_time_to_next_year(todays_date: datetime.date = datetime.date(2022, 4, 26), trading_days: int = 252) ‑> float
-
Parameters
- todays_date:
date
Optional. Reference date for calculation. - trading_days:
int
Optional. Number of trading days in a year. Defaults to 252.
Expand source code
def get_time_to_next_year(todays_date: date = today(), trading_days: int = 252) -> float: """ Parameters ---------- 1. **todays_date**: ``date`` *Optional*. Reference date for calculation. 2. **trading_days**: ``int`` *Optional*. Number of trading days in a year. Defaults to 252. """ # TODO: what if first day of year falls on non-trading day? # which it will, by definition. fuckwit. next_year = datetime.datetime(year=todays_date.year+1, day=1, month=1) return ((next_year - todays_date).days / trading_days)
- todays_date:
def increment_date_by_business_days(start_date: Union[datetime.date, str], business_days: int) ‑> datetime.date
-
Expand source code
def increment_date_by_business_days(start_date: Union[date, str], business_days: int) -> date: start_date = validate_date(start_date) while business_days > 0: if is_trading_date(start_date): business_days -= 1 start_date += datetime.timedelta(days=1) return start_date
def intersect_with_trading_dates(date_key_dict: dict) ‑> dict
-
Expand source code
def intersect_with_trading_dates(date_key_dict: dict) -> dict: return {this_date: date_key_dict[this_date] for this_date in date_key_dict if is_trading_date(this_date)}
def is_date_holiday(this_date: Union[datetime.date, str]) ‑> bool
-
Expand source code
def is_date_holiday(this_date: Union[date, str]) -> bool: this_date = validate_date(this_date) us_holidays = holidays.UnitedStates(years=this_date.year) # generate list without columbus day and veterans day since markets are open on those days trading_holidays = [ "Columbus Day", "Columbus Day (Observed)", "Veterans Day", "Veterans Day (Observed)"] # markets are open # see here: https://www.barrons.com/articles/stock-market-open-close-new-years-eve-monday-hours-51640891577 if datetime.datetime(year=this_date.year+1, month=1, day=1).weekday() in [5, 6]: trading_holidays += ["New Year's Day (Observed)"] custom_holidays = [that_date for that_date in list( us_holidays) if us_holidays[that_date] not in trading_holidays] # add good friday to list since markets are closed on good friday custom_holidays.append(easter.easter( year=this_date.year) - datetime.timedelta(days=2)) return (this_date in custom_holidays)
def is_date_today(this_date: Union[datetime.date, str]) ‑> bool
-
Expand source code
def is_date_today(this_date: Union[date, str]) -> bool: return (validate_date(this_date) == datetime.date.today())
def is_date_weekend(this_date: Union[datetime.date, str]) ‑> bool
-
Expand source code
def is_date_weekend(this_date: Union[date, str]) -> bool: return validate_date(this_date).weekday() in [5, 6]
def is_future_date(this_date: Union[datetime.date, str]) ‑> bool
-
Expand source code
def is_future_date(this_date: Union[date, str]) -> bool: return (validate_date(this_date) - today()).days > 0
def is_trading_date(this_date: Union[datetime.date, str]) ‑> bool
-
Expand source code
def is_trading_date(this_date: Union[date, str]) -> bool: this_date = validate_date(this_date) return not is_date_weekend(this_date) and not is_date_holiday(this_date)
def last_close_date()
-
Expand source code
def last_close_date(): right_now = datetime.datetime.now() trading_close_today = right_now.replace(hour=16) if right_now > trading_close_today: return right_now.date() return get_previous_business_date(right_now.date())
def parse(date_string: str) ‑> Optional[datetime.date]
-
Converts a date string in the 'YYYY-MM-DD' format to a Python
datetime.date
.Expand source code
def parse(date_string: str) -> Union[date, None]: """ Converts a date string in the 'YYYY-MM-DD' format to a Python `datetime.date`. """ return datetime.datetime.strptime(date_string, DATE_FORMAT).date()
def this_date_or_last_trading_date(this_date: Union[datetime.date, str, ForwardRef(None)] = None) ‑> datetime.date
-
Expand source code
def this_date_or_last_trading_date(this_date: Union[date, str, None] = None) -> date: if this_date is None: return get_last_trading_date() this_date = validate_date(this_date) if is_date_holiday(this_date) or is_date_weekend(this_date): return get_previous_business_date(this_date) if is_date_today(this_date): return last_close_date() return this_date
def to_string(this_date: Optional[datetime.date] = None) ‑> str
-
Returns a datetime formatted as 'YYYY-MM-DD'. If no date is provided, function will return today's formatted date.
Expand source code
def to_string(this_date: Union[date, None] = None) -> str: """ Returns a datetime formatted as 'YYYY-MM-DD'. If no date is provided, function will return today's formatted date. """ if this_date is None: return to_string(today()) return datetime.datetime.strftime(this_date, DATE_FORMAT)
def today() ‑> datetime.date
-
Returns today's date
Expand source code
def today() -> datetime.date: """ Returns today's date """ return datetime.date.today()
def truncate_future_from_date(this_date: Union[datetime.date, str]) ‑> datetime.date
-
Expand source code
def truncate_future_from_date(this_date: Union[date, str]) -> datetime.date: this_date = validate_date(this_date) if is_future_date(this_date): return today() return this_date
def validate_date(this_date: Any) ‑> datetime.date
-
Expand source code
def validate_date(this_date: Any) -> date: if isinstance(this_date, str): return parse(this_date) if not isinstance(this_date, date): raise ValueError( f'{this_date} is neither date nor \'{DATE_FORMAT}\' formatted string') return this_date
def validate_date_list(dates: List[Union[datetime.date, str]]) ‑> Optional[List[datetime.date]]
-
Raises
- ValueError If the supplied list of dates contains unexpected data types, this error will be thrown.
Expand source code
def validate_date_list(dates: Union[List[Union[datetime.date, str]]]) -> Union[List[datetime.date], None]: """ Raises ------ 1. **ValueError** If the supplied list of dates contains unexpected data types, this error will be thrown. """ verified_dates = [] for this_date in dates: if isinstance(this_date, str): verified_dates.append(parse(this_date)) continue if isinstance(this_date, datetime.date): verified_dates.append(this_date) continue raise ValueError( f'{this_date} is neither date nor \'{DATE_FORMAT}\'formatted string') return verified_dates
def validate_date_range(start_date: Any, end_date: Any) ‑> Tuple[datetime.date, datetime.date]
-
Expand source code
def validate_date_range(start_date: Any, end_date: Any) -> Tuple[date, date]: if isinstance(start_date, str): start_date = parse(start_date) elif not isinstance(start_date, date): raise ValueError( f'{start_date} is neither date nor \'{DATE_FORMAT}\' formatted string') if isinstance(end_date, str): end_date = parse(end_date) elif not isinstance(end_date, date): raise ValueError( f'{end_date} is neither date nor \'{DATE_FORMAT}\' formatted string') return validate_order_of_dates(start_date, end_date)
def validate_order_of_dates(start_date: datetime.date, end_date: datetime.date) ‑> Tuple[datetime.date, datetime.date]
-
Returns the inputted dates as an tuple ordered from earliest to latest.
Expand source code
def validate_order_of_dates(start_date: date, end_date: date) -> Tuple[date, date]: """ Returns the inputted dates as an tuple ordered from earliest to latest. """ delta = (end_date - start_date).days if delta < 0: return end_date, start_date return start_date, end_date
def weekends_between(start_date: Union[datetime.date, str], end_date: Union[datetime.date, str]) ‑> List[int]
-
Expand source code
def weekends_between(start_date: Union[date, str], end_date: Union[date, str]) -> List[int]: start_date, end_date = validate_date_range(start_date, end_date) dates = dates_between(start_date, end_date) return len([1 for day in dates if day.weekday() > 4])