Beispiel #1
0
 def test_yields_from_forward_rates_no_batches(self):
     dtypes = [np.float64, np.float32]
     for dtype in dtypes:
         times = np.array([0.25, 0.5, 1.0, 1.25, 1.5, 2.0, 2.5],
                          dtype=dtype)
         forward_rates = np.array(
             [0.04, 0.042, 0.047, 0.054, 0.046, 0.05, 0.062], dtype=dtype)
         expected_rates = np.array(
             [0.04, 0.041, 0.044, 0.046, 0.046, 0.047, 0.050], dtype=dtype)
         actual_rates = self.evaluate(
             forwards.yields_from_forward_rates(forward_rates,
                                                times,
                                                dtype=dtype))
         np.testing.assert_allclose(actual_rates, expected_rates, atol=1e-6)
Beispiel #2
0
 def test_yields_from_forwards(self):
     dtypes = [np.float64, np.float32]
     for dtype in dtypes:
         groups = np.array([0, 0, 0, 1, 1, 1, 1])
         times = np.array([0.25, 0.5, 1.0, 0.25, 0.5, 1.0, 1.5],
                          dtype=dtype)
         forward_rates = np.array(
             [0.04, 0.042, 0.047, 0.022, 0.028, 0.031, 0.052], dtype=dtype)
         expected_rates = np.array(
             [0.04, 0.041, 0.044, 0.022, 0.025, 0.028, 0.036], dtype=dtype)
         actual_rates = self.evaluate(
             forwards.yields_from_forward_rates(forward_rates,
                                                times,
                                                groups=groups,
                                                dtype=dtype))
         np.testing.assert_allclose(actual_rates, expected_rates, atol=1e-6)
def interpolate_yields(interpolation_times,
                       reference_times,
                       yields=None,
                       discrete_forwards=None,
                       validate_args=False,
                       dtype=None,
                       name=None):
    """Interpolates the yield curve to the supplied times.

    Applies the Hagan West procedure to interpolate either a zero coupon yield
    curve or a discrete forward curve to a given set of times.
    A zero coupon yield curve is specified by a set
    of times and the yields on zero coupon bonds expiring at those
    times. A discrete forward rate curve specifies the interest rate that
    applies between two times in the future as seen from the current time.
    The relation between the two sets of curve is as follows. Suppose the
    yields on zero coupon bonds expiring at times `[t_1, ..., t_n]` are
    `[r_1, ..., r_n]`, then the forward rate between time `[t_i, t_{i+1}]` is
    denoted `f(0; t_i, t_{i+1})` and given by

    ```None
      f(0; t_i, t_{i+1}) = (r_{i+1} t_{i+1} - r_i t_i) / (t_{i+1} - t_i)
    ```

    This function uses the Hagan West algorithm to perform the interpolation.
    The interpolation proceeds in two steps. Firstly the discrete forward
    curve is bootstrapped and an instantaneous forward curve is built. From the
    instantaneous forward curve, the interpolated yield values are inferred
    using the relation:

    ```None
      r(t) = (1/t) * Integrate[ f(s), 0 <= s <= t]
    ```

    The above equation connects the instantaneous forward curve `f(t)` to the
    yield curve `r(t)`. The Hagan West procedure uses the Monotone Convex
    interpolation to create a continuous forward curve. This is then integrated
    to compute the implied yield rate.

    For more details on the interpolation procedure, see Ref. [1].

  #### Example

  ```python
    dtype = np.float64
    reference_times = np.array([1.0, 2.0, 3.0, 4.0], dtype=dtype)
    yields = np.array([5.0, 4.75, 4.53333333, 4.775], dtype=dtype)

    # Times for which the interpolated values are required.
    interpolation_times = np.array([0.25, 0.5, 1.0, 2.0], dtype=dtype)

    interpolated = interpolate_yields(
        interpolation_times, reference_times, yields=yields)
    # Produces [5.1171875, 5.09375, 5.0, 4.75]
  ```

  #### References:

  [1]: Patrick Hagan & Graeme West. Methods for Constructing a Yield Curve.
    Wilmott Magazine, pp. 70-81. May 2008.
    https://www.researchgate.net/profile/Patrick_Hagan3/publication/228463045_Methods_for_constructing_a_yield_curve/links/54db8cda0cf23fe133ad4d01.pdf

  Args:
    interpolation_times: Non-negative rank 1 `Tensor` of any size. The times for
      which the interpolation has to be performed.
    reference_times: Strictly positive rank 1 `Tensor` of real dtype containing
      increasing values. The expiry times of the underlying zero coupon bonds.
    yields: Optional rank 1 `Tensor` of the same shape and dtype as
      `reference_times`, if supplied. The yield rate of zero coupon bonds
      expiring at the corresponding time in the `reference_times`. Either this
      argument or the `discrete_forwards` must be supplied (but not both).
      Default value: None.
    discrete_forwards: Optional rank 1 `Tensor` of the same shape and dtype as
      `reference_times`, if supplied. The `i`th component of the `Tensor` is the
      forward rate that applies between `reference_times[i-1]` and
      `reference_times[i]` for `i>0` and between time `0` and
      `reference_times[0]` for `i=0`. Either this argument or the `yields` must
      be specified (but not both).
      Default value: None.
    validate_args: Python bool. If true, adds control dependencies to check that
      the `times` are bounded by the `reference_times`.
      Default value: False
    dtype: `tf.Dtype` to use when converting arguments to `Tensor`s. If not
      supplied, the default Tensorflow conversion will take place. Note that
      this argument does not do any casting.
      Default value: None.
    name: Python `str` name prefixed to Ops created by this class.
      Default value: None which is mapped to the default name
        'interpolate_forward_rate'.

  Returns:
      interpolated_forwards: Rank 1 `Tensor` of the same size and dtype as the
        `interpolation_times`. The interpolated instantaneous forwards at the
        `interpolation_times`.

  Raises:
    ValueError if neither `yields` nor `discrete_forwards` are specified or if
    both are specified.
  """

    if (yields is None) == (discrete_forwards is None):
        raise ValueError('Exactly one of yields or discrete forwards must'
                         ' be supplied.')

    with tf.compat.v1.name_scope(name,
                                 default_name='interpolate_forward_rate',
                                 values=[
                                     interpolation_times, reference_times,
                                     yields, discrete_forwards
                                 ]):
        if discrete_forwards is not None:
            discrete_forwards = tf.convert_to_tensor(discrete_forwards,
                                                     dtype=dtype)
            reference_yields = forwards.yields_from_forward_rates(
                discrete_forwards, reference_times, dtype=dtype)
        reference_times = tf.convert_to_tensor(reference_times, dtype=dtype)
        interpolation_times = tf.convert_to_tensor(interpolation_times,
                                                   dtype=dtype)

        if yields is not None:
            reference_yields = tf.convert_to_tensor(yields, dtype=dtype)
            discrete_forwards = forwards.forward_rates_from_yields(
                reference_yields, reference_times, dtype=dtype)

        _, integrated_adjustments = interpolate(interpolation_times,
                                                discrete_forwards,
                                                reference_times,
                                                validate_args=validate_args,
                                                dtype=dtype)

        extended_times = tf.concat([[0.0], reference_times], axis=0)
        extended_yields = tf.concat([[0.0], reference_yields], axis=0)
        intervals = piecewise.find_interval_index(interpolation_times,
                                                  extended_times,
                                                  last_interval_is_closed=True)
        base_values = tf.gather(extended_yields * extended_times, intervals)
        interpolated = tf.math.divide_no_nan(
            base_values + integrated_adjustments, interpolation_times)
        return interpolated