def ir_delta_parallel_leg(
            self,
            leg: Union[cashflow_streams.FloatingCashflowStream,
                       cashflow_streams.FixedCashflowStream],
            processed_market_data: pmd.ProcessedMarketData,
            curve_type: Optional[curve_types.CurveType] = None,
            shock_size: Optional[float] = None) -> tf.Tensor:
        """Computes delta wrt to the curve parallel perturbation for a leg."""
        # TODO(b/160671927): Find a better way to update market data entries.
        market_bumped = copy.copy(processed_market_data)
        reference_curve = None
        reference_curve_type = None
        if curve_type is None:
            # Extract discount and reference curves
            curve_type = leg.discount_curve_type
            if isinstance(leg, cashflow_streams.FloatingCashflowStream):
                reference_curve_type = leg.reference_curve_type
                reference_curve = processed_market_data.yield_curve(
                    reference_curve_type)

        discount_curve = processed_market_data.yield_curve(curve_type)
        # IR delta is the sensitivity wrt the yield perturbation
        yields, times = market_data_utils.get_yield_and_time(
            discount_curve, processed_market_data.date, self._dtype)
        # Extract yields for reference curve, if needed
        if reference_curve is not None:
            reference_yields, reference_times = market_data_utils.get_yield_and_time(
                reference_curve, processed_market_data.date, self._dtype)

        def price_fn(bump):
            """Prices the leg with a given bump."""
            discount_factors = (1 + yields + bump)**(-times)
            discount_curve.set_discount_factor_nodes(discount_factors)

            def _discount_curve_fn(input_curve_type):
                """Updates discount curve."""
                if input_curve_type == curve_type:
                    return discount_curve
                elif input_curve_type == reference_curve_type:
                    reference_discount_factors = (1 + reference_yields +
                                                  bump)**(-reference_times)
                    reference_curve.set_discount_factor_nodes(
                        reference_discount_factors)
                    return reference_curve
                else:
                    return processed_market_data.yield_curve(curve_type)

            market_bumped.yield_curve = _discount_curve_fn
            return leg.price(market_bumped)

        if shock_size is None:
            bump = tf.constant(0, dtype=self._dtype, name="bump")
            return tff_math.fwd_gradient(price_fn, bump)
        shock_size = tf.convert_to_tensor(shock_size,
                                          dtype=self._dtype,
                                          name="shock_size")
        price_no_bump = leg.price(processed_market_data)
        price_with_bump = price_fn(shock_size)
        delta = (price_with_bump - price_no_bump) / shock_size
        return delta
    def price(self,
              market: pmd.ProcessedMarketData,
              name: Optional[str] = None) -> types.FloatTensor:
        """Returns the present value of the stream on the valuation date.

    Args:
      market: An instance of `ProcessedMarketData`.
      name: Python str. The name to give to the ops created by this function.
        Default value: `None` which maps to 'price'.

    Returns:
      A `Tensor` of shape `batch_shape`  containing the modeled price of each
      FRA contract based on the input market data.
    """
        name = name or (self._name + "_price")
        with tf.name_scope(name):
            discount_curve = market.yield_curve(self._discount_curve_type)
            reference_curve = market.yield_curve(self._reference_curve_type)
            fwd_rate = reference_curve.forward_rate(
                self._accrual_start_date,
                self._accrual_end_date,
                day_count_fraction=self._daycount_fractions)
            discount_at_settlement = discount_curve.discount_factor(
                self._accrual_start_date)
            discount_at_settlement = tf.where(
                self._daycount_fractions > 0., discount_at_settlement,
                tf.zeros_like(discount_at_settlement))

            return (self._short_position * discount_at_settlement *
                    self._notional_amount * (fwd_rate - self._fixed_rate) *
                    self._daycount_fractions /
                    (1. + self._daycount_fractions * fwd_rate))
def get_discount_curve(discount_curve_types: List[
    Union[curve_types_lib.RiskFreeCurve,
          curve_types_lib.RateIndexCurve]], market: pmd.ProcessedMarketData,
                       mask: List[int]) -> rate_curve.RateCurve:
    """Builds a batched discount curve.

  Given a list of discount curve an integer mask, creates a discount curve
  object to compute discount factors against the list of discount curves.

  #### Example
  ```none
  curve_types = [RiskFreeCurve("USD"), RiskFreeCurve("AUD")]
  # A mask to price a batch of 7 instruments with the corresponding discount
  # curves ["USD", "AUD", "AUD", "AUD" "USD", "USD", "AUD"].
  mask = [0, 1, 1, 1, 0, 0, 1]
  market = MarketDataDict(...)
  get_discount_curve(curve_types, market, mask)
  # Returns a RateCurve object that can compute a discount factors for a
  # batch of 7 dates.
  ```

  Args:
    discount_curve_types: A list of curve types.
    market: an instance of the processed market data.
    mask: An integer mask.

  Returns:
    An instance of `RateCurve`.
  """
    discount_curves = [
        market.yield_curve(curve_type) for curve_type in discount_curve_types
    ]
    discounts = []
    dates = []
    interpolation_method = None
    interpolate_rates = None
    for curve in discount_curves:
        discount, date = curve.discount_factors_and_dates()
        discounts.append(discount)
        dates.append(date)
        interpolation_method = curve.interpolation_method
        interpolate_rates = curve.interpolate_rates

    all_discounts = tf.stack(pad.pad_tensors(discounts), axis=0)
    all_dates = pad.pad_date_tensors(dates)
    all_dates = dateslib.DateTensor.stack(dates, axis=0)
    prepare_discounts = tf.gather(all_discounts, mask)
    prepare_dates = dateslib.dates_from_ordinals(
        tf.gather(all_dates.ordinal(), mask))
    # All curves are assumed to have the same interpolation method
    # TODO(b/168411153): Extend to the case with multiple curve configs.
    discount_curve = rate_curve.RateCurve(prepare_dates,
                                          prepare_discounts,
                                          market.date,
                                          interpolator=interpolation_method,
                                          interpolate_rates=interpolate_rates)
    return discount_curve
Example #4
0
 def _annuity(self, market: pmd.ProcessedMarketData) -> tf.Tensor:
   """Returns the annuity of each swap on the vauation date."""
   num_fixed_legs = 0
   if isinstance(self._pay_leg, cashflow_streams.FixedCashflowStream):
     fixed_leg = self._pay_leg
     num_fixed_legs += 1
   if isinstance(self._receive_leg, cashflow_streams.FixedCashflowStream):
     fixed_leg = self._receive_leg
     num_fixed_legs += 1
   if num_fixed_legs == 0:
     raise ValueError("Swap does not have a fixed leg.")
   if num_fixed_legs == 2:
     raise ValueError("Swap should not have both fixed leg.")
   discount_curve = market.yield_curve(self._discount_curve_type)
   discount_factors = discount_curve.discount_factor(
       fixed_leg.cashflow_dates)
   return tf.math.reduce_sum(
       discount_factors * fixed_leg.daycount_fractions, axis=-1)
Example #5
0
    def forward_rates(self,
                      market: pmd.ProcessedMarketData,
                      name: Optional[str] = None
                      ) -> Tuple[types.DateTensor, types.FloatTensor]:
        """Returns forward rates for the floating leg.

    Args:
      market: An instance of `ProcessedMarketData`.
      name: Python str. The name to give to the ops created by this function.
        Default value: `None` which maps to 'forward_rates'.

    Returns:
      A tuple of two `Tensor`s of shape `batch_shape + [num_cashflows]`
      containing the dates and the corresponding forward rates for each stream
      based on the input market data.
    """
        name = name or (self._name + "_forward_rates")
        with tf.name_scope(name):
            reference_curve = market.yield_curve(self._reference_curve_type)
            valuation_date = dateslib.convert_to_date_tensor(market.date)
            # TODO(cyrilchimisov): vectorize fixing computation
            past_fixing = market.fixings(self._start_date,
                                         self._reference_curve_type,
                                         self._reset_frequency)
            forward_rates = reference_curve.forward_rate(
                self._accrual_start_date,
                self._accrual_end_date,
                day_count_fraction=self._daycount_fractions)
            forward_rates = tf.where(self._daycount_fractions > 0.,
                                     forward_rates,
                                     tf.zeros_like(forward_rates))
            # If coupon end date is before the valuation date, the payment is in the
            # past. If valuation date is between coupon start date and coupon end
            # date, then the rate has been fixed but not paid. Otherwise the rate is
            # not fixed and should be read from the curve.
            forward_rates = tf.where(
                self._coupon_end_dates < valuation_date,
                tf.constant(0, dtype=self._dtype),
                tf.where(self._coupon_start_dates >= valuation_date,
                         forward_rates, tf.expand_dims(past_fixing, axis=-1)))
            return self._coupon_end_dates, forward_rates
Example #6
0
    def price(self,
              market: pmd.ProcessedMarketData,
              name: Optional[str] = None):
        """Returns the present value of the stream on the valuation date.

    Args:
      market: An instance of `ProcessedMarketData`.
      name: Python str. The name to give to the ops created by this function.
        Default value: `None` which maps to 'price'.

    Returns:
      A `Tensor` of shape `batch_shape`  containing the modeled price of each
      stream based on the input market data.
    """
        name = name or (self._name + "_price")
        with tf.name_scope(name):
            discount_curve = market.yield_curve(self._discount_curve_type)
            discount_factors = discount_curve.discount_factor(
                self._payment_dates)
            _, cashflows = self.cashflows(market)
            # Cashflow present values
            cashflow_pvs = (cashflows * discount_factors)
            return tf.math.reduce_sum(cashflow_pvs, axis=1)