Ejemplo n.º 1
0
def pad_date_tensors(date_tensors, name=None):
  """Pads the innermost dimension of `DateTensor`s to a common shape.

  Given a list of `DateTensor`s of shapes `batch_shape_i + [n_i]`, pads the
  innermost dimension of each corresponding ordinal tensor to
  `batch_shape_i + [max(n_i)]`. For each ordinal tensor `t`, the padding is done
  with values `t[..., -1]`.

  ### Example
  ```python
  x = [(2020, 1, 1), (2021, 2, 2)]
  y = [(2019, 5, 5), (2028, 10, 21), (2028, 11, 10)]
  pad_date_tensors([x, y])
  # Expected: [DateTensor: [(2020, 1, 1), (2021, 2, 2), (2021, 2, 2)],
  #            DateTensor: [(2019, 5, 5), (2028, 10, 21), (2028, 11, 10)]]
  ```

  Args:
    date_tensors: a list of tensors of shapes `batch_shape_i + [n_i]`.
    name: Python string. The name to give to the ops created by this class.
      Default value: `None` which maps to the default name `pad_date_tensors`.
  Returns:
    A list of `DateTensor`s of shape `batch_shape_i + [max(n_i)]`.
  """
  name = name or "pad_date_tensors"
  with tf.name_scope(name):
    ordinals = [date_tensor.convert_to_date_tensor(t).ordinal()
                for t in date_tensors]
    padded_tensors = pad_tensors(ordinals)
    return [date_tensor.from_ordinals(p) for p in padded_tensors]
Ejemplo n.º 2
0
def actual_actual_isda(*,
                       start_date,
                       end_date,
                       schedule_info=None,
                       dtype=None,
                       name=None):
    """Computes the year fraction between the specified dates.

  Computes the year fraction between the dates by dividing the actual number of
  days in a leap year by 366 and the actual number of days in a standard year by
  365.

  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.

  https://en.wikipedia.org/wiki/Day_count_convention#Actual/Actual_ISDA

  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_actual_isda` 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/Actual ISDA
    convention.
  """
    del schedule_info
    with tf.name_scope(name or 'actual_actual_isda'):
        end_date = dt.convert_to_date_tensor(end_date)
        start_date = dt.convert_to_date_tensor(start_date)
        dtype = dtype or tf.float32
        (days_in_leap_years,
         days_in_nonleap_years) = du.days_in_leap_and_nonleap_years_between(
             start_date, end_date)
        # Cast to the target dtype
        days_in_leap_years = tf.cast(days_in_leap_years, dtype=dtype)
        days_in_nonleap_years = tf.cast(days_in_nonleap_years, dtype=dtype)
        return days_in_leap_years / 366 + days_in_nonleap_years / 365
Ejemplo n.º 3
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
    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()
Ejemplo n.º 7
0
    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))
Ejemplo n.º 8
0
    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))
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
 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]