def actual_365_actual(*, start_date, end_date, schedule_info=None, dtype=None, name=None): """Computes the year fraction between the specified dates. The actual/365 actual convention specifies the year fraction between the start and end date as the actual number of days between the two dates divided 365 if no leap day is contained in the date range and 366 otherwise. When determining whether a leap day is contained in the date range, 'start_date' is excluded and 'end_date' is included. Note that the schedule info is not needed for this convention and is ignored if supplied. Args: start_date: A `DateTensor` object of any shape. end_date: A `DateTensor` object of compatible shape with `start_date`. schedule_info: The schedule info. Ignored for this convention. dtype: The dtype of the result. Either `tf.float32` or `tf.float64`. If not supplied, `tf.float32` is returned. name: Python `str` name prefixed to ops created by this function. If not supplied, `actual_365_actual` is used. Returns: A real `Tensor` of supplied `dtype` and shape of `start_date`. The year fraction between the start and end date as computed by Actual/365 Actual convention. """ del schedule_info with tf.name_scope(name or 'actual_365_actual'): end_date = dt.convert_to_date_tensor(end_date) start_date = dt.convert_to_date_tensor(start_date) dtype = dtype or tf.constant(0.).dtype actual_days = tf.cast(start_date.days_until(end_date), dtype=dtype) # Add a day to start_date and end_date so that start_date is excluded and # end_date is included. day = periods.day() leap_days_between = du.leap_days_between( start_date=start_date + day, end_date=end_date + day) denominator = tf.cast(tf.where(leap_days_between > 0, 366, 365), dtype=dtype) return actual_days / denominator
def actual_365_fixed(*, start_date, end_date, schedule_info=None, dtype=None, name=None): """Computes the year fraction between the specified dates. The actual/365 convention specifies the year fraction between the start and end date as the actual number of days between the two dates divided by 365. Note that the schedule info is not needed for this convention and is ignored if supplied. For more details see: https://en.wikipedia.org/wiki/Day_count_convention#Actual/365_Fixed Args: start_date: A `DateTensor` object of any shape. end_date: A `DateTensor` object of compatible shape with `start_date`. schedule_info: The schedule info. Ignored for this convention. dtype: The dtype of the result. Either `tf.float32` or `tf.float64`. If not supplied, `tf.float32` is returned. name: Python `str` name prefixed to ops created by this function. If not supplied, `actual_365_fixed` is used. Returns: A real `Tensor` of supplied `dtype` and shape of `start_date`. The year fraction between the start and end date as computed by Actual/365 fixed convention. """ del schedule_info with tf.name_scope(name or 'actual_365_fixed'): end_date = dt.convert_to_date_tensor(end_date) start_date = dt.convert_to_date_tensor(start_date) dtype = dtype or tf.constant(0.).dtype actual_days = tf.cast(start_date.days_until(end_date), dtype=dtype) return actual_days / 365
def business_days_between(self, from_dates, to_dates): """Calculates number of business between pairs of dates. For each pair, the initial date is included in the difference, and the final date is excluded. If the final date is the same or earlier than the initial date, zero is returned. Args: from_dates: `DateTensor` of initial dates. to_dates: `DateTensor` of final dates, should be broadcastable to `from_dates`. Returns: An int32 Tensor with the number of business days between the corresponding pairs of dates. """ from_biz, from_is_bizday = self._to_biz_space( dt.convert_to_date_tensor(from_dates).ordinal()) to_biz, to_is_bizday = self._to_biz_space( dt.convert_to_date_tensor(to_dates).ordinal()) from_biz = tf.where(from_is_bizday, from_biz, from_biz + 1) to_biz = tf.where(to_is_bizday, to_biz, to_biz + 1) return tf.math.maximum(to_biz - from_biz, 0)
def __init__( self, weekend_mask=None, holidays=None, start_year=None, end_year=None): """Initializer. Args: weekend_mask: Tensor of 7 elements, where "0" means work day and "1" - day off. The first element is Monday. By default, no weekends are applied. Some of the common weekend patterns are defined in `dates.WeekendMask`. Default value: None which maps to no weekend days. holidays: Defines the holidays that are added to the weekends defined by `weekend_mask`. An instance of `dates.DateTensor` or an object convertible to `DateTensor`. Default value: None which means no holidays other than those implied by the weekends (if any). start_year: Integer giving the earliest year this calendar includes. If `holidays` is specified, then `start_year` and `end_year` are ignored, and the boundaries are derived from `holidays`. If `holidays` is `None`, both `start_year` and `end_year` must be specified. end_year: Integer giving the latest year this calendar includes. If `holidays` is specified, then `start_year` and `end_year` are ignored, and the boundaries are derived from `holidays`. If `holidays` is `None`, both `start_year` and `end_year` must be specified. """ self._weekend_mask = tf.convert_to_tensor(weekend_mask or constants.WeekendMask.NONE) if holidays is None: self._holidays = None else: self._holidays = dt.convert_to_date_tensor(holidays) start_year, end_year = _resolve_calendar_boundaries(self._holidays, start_year, end_year) self._ordinal_offset = dt.from_year_month_day(start_year, 1, 1).ordinal() self._calendar_size = ( dt.from_year_month_day(end_year + 1, 1, 1).ordinal() - self._ordinal_offset) # Precomputed tables. These are constant 1D Tensors, mapping each day in the # [start_year, end_year] period to some quantity of interest, e.g. next # business day. The tables should be indexed with # `date.ordinal - self._offset`. All tables are computed lazily. # All tables have an extra element at the beginning and the end, see comment # in _compute_is_bus_day_table(). self._table_cache = _TableCache()
def roll_to_business_day(self, date_tensor, roll_convention): """Rolls the given dates to business dates according to given convention. Args: date_tensor: `DateTensor` of dates to roll from. roll_convention: BusinessDayConvention. Determines how to roll a date that falls on a holiday. Returns: The resulting `DateTensor`. """ if roll_convention == constants.BusinessDayConvention.NONE: return date_tensor ordinals = dt.convert_to_date_tensor(date_tensor).ordinal() biz_days, is_bizday = self._to_biz_space(ordinals) biz_days_rolled = self._apply_roll_biz_space(date_tensor, biz_days, is_bizday, roll_convention) return dt.from_ordinals(self._from_biz_space(biz_days_rolled))
def add_business_days( self, date_tensor, num_days, roll_convention=constants.BusinessDayConvention.NONE): """Adds given number of business days to given dates. Note that this is different from calling `add_period_and_roll` with PeriodType.DAY. For example, adding 5 business days to Monday gives the next Monday (unless there are holidays on this week or next Monday). Adding 5 days and rolling means landing on Saturday and then rolling either to next Monday or to Friday of the same week, depending on the roll convention. If any of the dates in `date_tensor` are not business days, they will be rolled to business days before doing the addition. If `roll_convention` is `NONE`, and any dates are not business days, an exception is raised. Args: date_tensor: `DateTensor` of dates to advance from. num_days: Tensor of int32 type broadcastable to `date_tensor`. roll_convention: BusinessDayConvention. Determines how to roll a date that falls on a holiday. Returns: The resulting `DateTensor`. """ control_deps = [] biz_days, is_bizday = self._to_biz_space( dt.convert_to_date_tensor(date_tensor).ordinal()) if roll_convention == constants.BusinessDayConvention.NONE: control_deps.append( tf.debugging.assert_equal( is_bizday, True, message='Non business starting day with no roll convention.' )) with tf.compat.v1.control_dependencies(control_deps): biz_days_rolled = self._apply_roll_biz_space( date_tensor, biz_days, is_bizday, roll_convention) return dt.from_ordinals( self._from_biz_space(biz_days_rolled + num_days))
def __init__(self, weekend_mask=None, holidays=None): """Initializer. Args: weekend_mask: Boolean `Tensor` of 7 elements one for each day of the week starting with Monday at index 0. A `True` value indicates the day is considered a weekend day and a `False` value implies a week day. Default value: None which means no weekends are applied. holidays: Defines the holidays that are added to the weekends defined by `weekend_mask`. An instance of `dates.DateTensor` or an object convertible to `DateTensor`. Default value: None which means no holidays other than those implied by the weekends (if any). """ if weekend_mask is not None: weekend_mask = tf.cast(weekend_mask, dtype=tf.bool) if holidays is not None: holidays = dt.convert_to_date_tensor(holidays).ordinal() self._to_biz_space, self._from_biz_space = hol.business_day_mappers( weekend_mask=weekend_mask, holidays=holidays)
def is_business_day(self, date_tensor): """Returns a tensor of bools for whether given dates are business days.""" ordinals = dt.convert_to_date_tensor(date_tensor).ordinal() return self._to_biz_space(ordinals)[1]