예제 #1
0
    def price(self,
              market: pmd.ProcessedMarketData,
              name: Optional[str] = None) -> types.FloatTensor:
        """Returns the present value of the American options.

    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
      American option contract based on the input market data.
    """
        name = name or (self._name + "_price")
        with tf.name_scope(name):
            discount_curve = cashflow_streams.get_discount_curve(
                self._discount_curve_type, market, self._discount_curve_mask)
            currencies = [
                cur.currency.value for cur in self._discount_curve_type
            ]
            vol_surface = equity_utils.get_vol_surface(currencies,
                                                       self._equity, market,
                                                       self._equity_mask)
            spots = tf.stack(market.spot(currencies, self._equity), axis=0)
            discount_factors = discount_curve.discount_factor(
                self._expiry_date.expand_dims(axis=-1))
            daycount_convention = discount_curve.daycount_convention
            day_count_fn = market_data_utils.get_daycount_fn(
                daycount_convention)
            if spots.shape.rank > 0:
                spots = tf.gather(spots, self._equity_mask)
            if self._model == "BS-LSM":
                # TODO(b/168798725): volatility should be time-dependent
                vols = vol_surface.volatility(
                    expiry_dates=self._expiry_date.expand_dims(axis=-1),
                    strike=tf.expand_dims(self._strike, axis=-1))
                prices = utils.bs_lsm_price(
                    spots=spots,
                    expiry_times=day_count_fn(start_date=market.date,
                                              end_date=self._expiry_date,
                                              dtype=self._dtype),
                    strikes=self._strike,
                    volatility=tf.squeeze(vols, axis=-1),
                    discount_factors=tf.squeeze(discount_factors),
                    is_call_option=self._is_call_option,
                    num_samples=self._num_samples,
                    num_exercise_times=self._num_exercise_times,
                    num_calibration_samples=self._num_calibration_samples,
                    seed=self._seed)
                return self._short_position * self._contract_amount * prices
            else:
                raise ValueError("Only BS-LSM model is suppoted. "
                                 "Supplied {}".format(self._model))
    def __init__(self,
                 valuation_date: types.DateTensor,
                 expiries: types.DateTensor,
                 strikes: types.FloatTensor,
                 volatilities: types.FloatTensor,
                 daycount_convention: Optional[
                     _DayCountConventionsProtoType] = None,
                 dtype: Optional[tf.DType] = None,
                 name: Optional[str] = None):
        """Initializes the volatility surface.

    Args:
      valuation_date: A `DateTensor` specifying the valuation (or
        settlement) date for the curve.
      expiries: A `DateTensor` containing the expiry dates on which the
        implied volatilities are specified. Should have a compatible shape with
        valuation_date.
      strikes: A `Tensor` of real dtype specifying the strikes corresponding to
        the input maturities. The shape of this input should match the shape of
        `expiries`.
      volatilities: A `Tensor` of real dtype specifying the volatilities
        corresponding to  the input maturities. The shape of this input should
        match the shape of `expiries`.
      daycount_convention: `DayCountConventions` to use for the interpolation
        purpose.
        Default value: `None` which maps to actual/365 day count convention.
      dtype: `tf.Dtype`. Optional input specifying the dtype of the `rates`
        input.
      name: Python str. The name to give to the ops created by this function.
        Default value: `None` which maps to 'rate_curve'.
    """
        self._name = name or "VolatilitySurface"
        self._dtype = dtype or tf.float64
        with tf.name_scope(self._name):
            self._daycount_convention = (daycount_convention
                                         or _DayCountConventions.ACTUAL_365)
            self._day_count_fn = utils.get_daycount_fn(
                self._daycount_convention)
            self._valuation_date = dateslib.convert_to_date_tensor(
                valuation_date)
            self._expiries = dateslib.convert_to_date_tensor(expiries)
            self._strikes = tf.convert_to_tensor(strikes,
                                                 dtype=self._dtype,
                                                 name="strikes")
            self._volatilities = tf.convert_to_tensor(volatilities,
                                                      dtype=self._dtype,
                                                      name="volatilities")
            expiry_times = self._day_count_fn(start_date=self._valuation_date,
                                              end_date=self._expiries,
                                              dtype=self._dtype)
            self._interpolator = interpolation_2d.Interpolation2D(
                expiry_times, strikes, volatilities, dtype=self._dtype)
def _get_fixings(start_dates, end_dates, reference_curve_types, reference_mask,
                 market):
    """Computes fixings for a list of reference curves."""
    num_curves = len(reference_curve_types)
    if num_curves > 1:
        # For each curve get corresponding cashflow indices
        split_indices = [
            tf.squeeze(tf.where(tf.equal(reference_mask, i)), -1)
            for i in range(num_curves)
        ]
    else:
        split_indices = [0]
    fixings = []
    start_dates_ordinal = start_dates.ordinal()
    end_dates_ordinal = end_dates.ordinal()
    for idx, reference_curve_type in zip(split_indices, reference_curve_types):
        if num_curves > 1:
            # Get all dates corresponding to the reference curve
            start_date = dateslib.dates_from_ordinals(
                tf.gather(start_dates_ordinal, idx))
            end_date = dateslib.dates_from_ordinals(
                tf.gather(end_dates_ordinal, idx))
        else:
            start_date = start_dates
            end_date = end_dates
        fixing, fixing_daycount = market.fixings(start_date,
                                                 reference_curve_type)
        if fixing_daycount is not None:
            fixing_daycount = market_data_utils.get_daycount_fn(
                fixing_daycount, dtype=market.dtype)
            year_fraction = fixing_daycount(start_date=start_date,
                                            end_date=end_date)
        else:
            year_fraction = 0.0
        fixings.append(fixing * year_fraction)
    fixings = pad.pad_tensors(fixings)
    all_indices = tf.concat(split_indices, axis=0)
    all_fixings = tf.concat(fixings, axis=0)
    if num_curves > 1:
        return tf.gather(all_fixings, tf.argsort(all_indices))
    else:
        return all_fixings
    def __init__(self,
                 short_position: types.BoolTensor,
                 currency: types.CurrencyProtoType,
                 fixing_date: types.DateTensor,
                 fixed_rate: types.FloatTensor,
                 notional_amount: types.FloatTensor,
                 daycount_convention: types.DayCountConventionsProtoType,
                 business_day_convention: types.BusinessDayConventionProtoType,
                 calendar: types.BankHolidaysProtoType,
                 rate_term: period_pb2.Period,
                 rate_index: types.RateIndexProtoType,
                 settlement_days: Optional[types.IntTensor] = 0,
                 fra_config: ForwardRateAgreementConfig = None,
                 batch_names: Optional[types.StringTensor] = None,
                 dtype: Optional[types.Dtype] = None,
                 name: Optional[str] = None):
        """Initializes the batch of FRA contracts.

    Args:
      short_position: Whether the contract holder lends or borrows the money.
        Default value: `True` which means that the contract holder lends the
        money at the fixed rate.
      currency: The denominated currency.
      fixing_date: A `DateTensor` specifying the dates on which forward
        rate will be fixed.
      fixed_rate: A `Tensor` of real dtype specifying the fixed rate
        payment agreed at the initiation of the individual contracts. The shape
        should be broadcastable with `fixed_rate`.
      notional_amount: A `Tensor` of real dtype broadcastable with fixed_rate
        specifying the notional amount for each contract. When the notional is
        specified as a scalar, it is assumed that all contracts have the same
        notional.
      daycount_convention: A `DayCountConvention` to determine how cashflows
        are accrued for each contract. Daycount is assumed to be the same for
        all contracts in a given batch.
      business_day_convention: A business count convention.
      calendar: A calendar to specify the weekend mask and bank holidays.
      rate_term: A tenor of the rate (usually Libor) that determines the
        floating cashflow.
      rate_index: A type of the floating leg. Either an instance of
        `RateIndexType` or a list of `RateIndexType`. If list, assumes that
        the batch shape is of rank 1 and the batch size is equal to the
        list length.
      settlement_days: An integer `Tensor` of the shape broadcastable with the
        shape of `fixing_date`.
      fra_config: Optional `ForwardRateAgreementConfig`.
      batch_names: A string `Tensor` of instrument names. Should be of shape
        `batch_shape + [2]` specying name and instrument type. This is useful
        when the `from_protos` method is used and the user needs to identify
        which instruments got batched together.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: `None` which maps to `float64`.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'forward_rate_agreement'.
    """
        self._name = name or "forward_rate_agreement"
        with tf.name_scope(self._name):
            if batch_names is not None:
                self._names = tf.convert_to_tensor(batch_names,
                                                   name="batch_names")
            else:
                self._names = None
            self._dtype = dtype or tf.float64
            ones = tf.constant(1, dtype=self._dtype)
            self._short_position = tf.where(short_position,
                                            ones,
                                            -ones,
                                            name="short_position")
            self._notional_amount = tf.convert_to_tensor(
                notional_amount, dtype=self._dtype, name="notional_amount")
            self._fixed_rate = tf.convert_to_tensor(fixed_rate,
                                                    dtype=self._dtype,
                                                    name="fixed_rate")
            settlement_days = tf.convert_to_tensor(settlement_days)
            # Business day roll convention and the end of month flag
            roll_convention, eom = market_data_utils.get_business_day_convention(
                business_day_convention)
            # TODO(b/160446193): Calendar is ignored at the moment
            calendar = dateslib.create_holiday_calendar(
                weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
            self._fixing_date = dateslib.convert_to_date_tensor(fixing_date)
            self._accrual_start_date = calendar.add_business_days(
                self._fixing_date,
                settlement_days,
                roll_convention=roll_convention)

            self._day_count_fn = market_data_utils.get_daycount_fn(
                daycount_convention)
            period = market_data_utils.get_period(rate_term)
            self._accrual_end_date = calendar.add_period_and_roll(
                self._accrual_start_date,
                period,
                roll_convention=roll_convention)
            if eom:
                self._accrual_end_date = self._accrual_end_date.to_end_of_month(
                )
            self._daycount_fractions = self._day_count_fn(
                start_date=self._accrual_start_date,
                end_date=self._accrual_end_date,
                dtype=self._dtype)
            self._settlement_days = settlement_days
            self._roll_convention = roll_convention
            # Get discount and reference curves
            if isinstance(rate_index, (list, tuple)):
                rate_index = rate_index[0]
            index_type = market_data_utils.get_index(rate_index,
                                                     period=rate_term)
            reference_curve_type = curve_types.CurveType(currency=currency,
                                                         index_type=index_type)
            if fra_config is not None:
                try:
                    self._discount_curve_type = fra_config.discounting_index
                except KeyError:
                    self._discount_curve_type = curve_types.CurveType(
                        currency=currency, index_type=curve_types.Index.OIS)
            else:
                self._discount_curve_type = curve_types.CurveType(
                    currency=currency, index_type=curve_types.Index.OIS)
            self._reference_curve_type = reference_curve_type
            self._batch_shape = self._daycount_fractions.shape.as_list()[:-1]
예제 #5
0
  def __init__(self,
               coupon_spec: coupon_specs.FixedCouponSpecs,
               discount_curve_type: _CurveType,
               start_date: types.DateTensor = None,
               end_date: types.DateTensor = None,
               discount_curve_mask: types.IntTensor = None,
               first_coupon_date: Optional[types.DateTensor] = None,
               penultimate_coupon_date: Optional[types.DateTensor] = None,
               schedule_fn: Optional[Callable[..., Any]] = None,
               schedule: Optional[types.DateTensor] = None,
               dtype: Optional[types.Dtype] = None,
               name: Optional[str] = None):
    """Initializes a batch of fixed cashflow streams.

    Args:
      coupon_spec: An instance of `FixedCouponSpecs` specifying the
        details of the coupon payment for the cashflow stream.
      discount_curve_type: An instance of `CurveType` or a list of those.
        If supplied as a list and `discount_curve_mask` is not supplied,
        the size of the list should be the same as the number of priced
        instruments.
      start_date: A `DateTensor` of `batch_shape` specifying the starting dates
        of the accrual of the first coupon of the cashflow stream. The shape of
        the input correspond to the number of streams being created.
        Either this of `schedule` should be supplied
        Default value: `None`
      end_date: A `DateTensor` of `batch_shape`specifying the end dates for
        accrual of the last coupon in each cashflow stream. The shape of the
        input should be the same as that of `start_date`.
        Either this of `schedule` should be supplied
        Default value: `None`
      discount_curve_mask: An optional integer `Tensor` of values ranging from
        `0` to `len(discount_curve_type)` and of shape `batch_shape`. Identifies
        a mapping between `discount_curve_type` list and the underlying
        instruments.
        Default value: `None`.
      first_coupon_date: An optional `DateTensor` specifying the payment dates
        of the first coupon of the cashflow stream. Use this input for cashflows
        with irregular first coupon. Should be of the same shape as
        `start_date`.
        Default value: None which implies regular first coupon.
      penultimate_coupon_date: An optional `DateTensor` specifying the payment
        dates of the penultimate (next to last) coupon of the cashflow
        stream. Use this input for cashflows with irregular last coupon.
        Should be of the same shape as `end_date`.
        Default value: None which implies regular last coupon.
      schedule_fn: A callable that accepts `start_date`, `end_date`,
        `coupon_frequency`, `settlement_days`, `first_coupon_date`, and
        `penultimate_coupon_date` as `Tensor`s and returns coupon payment
        days.
        Default value: `None`.
      schedule: A `DateTensor` of coupon payment dates.
        Default value: `None`.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: None which maps to the default dtype inferred by
        TensorFlow.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'fixed_cashflow_stream'.
    """
    self._name = name or "fixed_cashflow_stream"

    with tf.name_scope(self._name):
      curve_list = to_list(discount_curve_type)
      [
          self._discount_curve_type,
          self._mask
      ] = process_curve_types(curve_list, discount_curve_mask)

      if schedule is None:
        if (start_date is None) or (end_date is None):
          raise ValueError("If `schedule` is not supplied both "
                           "`start_date` and `end_date` should be supplied")

      if schedule is None:
        if isinstance(start_date, tf.Tensor):
          self._start_date = dateslib.dates_from_tensor(
              start_date)
        else:
          self._start_date = dateslib.convert_to_date_tensor(
              start_date)
        if isinstance(start_date, tf.Tensor):
          self._end_date = dateslib.dates_from_tensor(
              end_date)
        else:
          self._end_date = dateslib.convert_to_date_tensor(
              end_date)
        self._first_coupon_date = first_coupon_date
        self._penultimate_coupon_date = penultimate_coupon_date
        if self._first_coupon_date is not None:
          if isinstance(start_date, tf.Tensor):
            self._first_coupon_date = dateslib.dates_from_tensor(
                first_coupon_date)
          else:
            self._first_coupon_date = dateslib.convert_to_date_tensor(
                first_coupon_date)
        if self._penultimate_coupon_date is not None:
          if isinstance(start_date, tf.Tensor):
            self._penultimate_coupon_date = dateslib.dates_from_tensor(
                penultimate_coupon_date)
          else:
            self._penultimate_coupon_date = dateslib.convert_to_date_tensor(
                penultimate_coupon_date)

      # Update coupon frequency
      coupon_frequency = _get_attr(coupon_spec, "coupon_frequency")
      if isinstance(coupon_frequency, period_pb2.Period):
        coupon_frequency = market_data_utils.get_period(
            _get_attr(coupon_spec, "coupon_frequency"))
      if isinstance(coupon_frequency, (list, tuple)):
        coupon_frequency = market_data_utils.period_from_list(
            *_get_attr(coupon_spec, "coupon_frequency"))
      if isinstance(coupon_frequency, dict):
        coupon_frequency = market_data_utils.period_from_dict(
            _get_attr(coupon_spec, "coupon_frequency"))

      businessday_rule = coupon_spec.businessday_rule
      # Business day roll convention and the end of month flag
      roll_convention, eom = market_data_utils.get_business_day_convention(
          businessday_rule)

      notional = tf.convert_to_tensor(
          _get_attr(coupon_spec, "notional_amount"),
          dtype=dtype,
          name="notional")
      self._dtype = dtype or notional.dtype
      fixed_rate = tf.convert_to_tensor(_get_attr(coupon_spec, "fixed_rate"),
                                        dtype=self._dtype,
                                        name="fixed_rate")
      # TODO(b/160446193): Calendar is ignored and weekends only is used
      calendar = dateslib.create_holiday_calendar(
          weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
      daycount_fn = market_data_utils.get_daycount_fn(
          _get_attr(coupon_spec, "daycount_convention"), self._dtype)

      self._settlement_days = tf.convert_to_tensor(
          _get_attr(coupon_spec, "settlement_days"),
          dtype=tf.int32,
          name="settlement_days")

      if schedule is not None:
        if isinstance(start_date, tf.Tensor):
          coupon_dates = dateslib.dates_from_tensor(schedule)
        else:
          coupon_dates = dateslib.convert_to_date_tensor(schedule)
      elif schedule_fn is None:
        coupon_dates = _generate_schedule(
            start_date=self._start_date,
            end_date=self._end_date,
            coupon_frequency=coupon_frequency,
            roll_convention=roll_convention,
            calendar=calendar,
            settlement_days=self._settlement_days,
            end_of_month=eom,
            first_coupon_date=self._first_coupon_date,
            penultimate_coupon_date=self._penultimate_coupon_date)
      else:
        if first_coupon_date is not None:
          first_coupon_date = self._first_coupon_date.to_tensor()
        if penultimate_coupon_date is not None:
          penultimate_coupon_date = self._penultimate_coupon_date.to_tensor()
          coupon_dates = schedule_fn(
              start_date=self._start_date.to_tensor(),
              end_date=self._end_date.to_tensor(),
              coupon_frequency=coupon_frequency.quantity(),
              settlement_days=self._settlement_days,
              first_coupon_date=first_coupon_date,
              penultimate_coupon_date=penultimate_coupon_date)

      # Convert to DateTensor if the result comes from a tf.function
      coupon_dates = dateslib.convert_to_date_tensor(coupon_dates)

      self._batch_shape = tf.shape(coupon_dates.ordinal())[:-1]
      payment_dates = coupon_dates[..., 1:]

      daycount_fractions = daycount_fn(
          start_date=coupon_dates[..., :-1],
          end_date=coupon_dates[..., 1:])

      coupon_rate = tf.expand_dims(fixed_rate, axis=-1)

      self._num_cashflows = tf.shape(payment_dates.ordinal())[-1]
      self._payment_dates = payment_dates
      self._notional = notional
      self._daycount_fractions = daycount_fractions
      self._coupon_rate = coupon_rate
      self._calendar = coupon_rate
      self._fixed_rate = tf.convert_to_tensor(fixed_rate, dtype=self._dtype)
      self._daycount_fn = daycount_fn
    def __init__(self,
                 coupon_spec: coupon_specs.FloatCouponSpecs,
                 discount_curve_type: _CurveType,
                 start_date: types.DateTensor = None,
                 end_date: types.DateTensor = None,
                 discount_curve_mask: types.IntTensor = None,
                 rate_index_curves: curve_types_lib.RateIndexCurve = None,
                 reference_mask: types.IntTensor = None,
                 first_coupon_date: Optional[types.DateTensor] = None,
                 penultimate_coupon_date: Optional[types.DateTensor] = None,
                 schedule_fn: Optional[Callable[..., Any]] = None,
                 schedule: Optional[types.DateTensor] = None,
                 dtype: Optional[types.Dtype] = None,
                 name: Optional[str] = None):
        """Initializes a batch of floating cashflow streams.

    Args:
      coupon_spec: An instance of `FloatCouponSpecs` specifying the
        details of the coupon payment for the cashflow stream.
      discount_curve_type: An instance of `CurveType` or a list of those.
        If supplied as a list and `discount_curve_mask` is not supplied,
        the size of the list should be the same as the number of priced
        instruments. Defines discount curves for the instruments.
      start_date: A `DateTensor` of `batch_shape` specifying the starting dates
        of the accrual of the first coupon of the cashflow stream. The shape of
        the input correspond to the number of streams being created.
        Either this of `schedule` should be supplied.
        When passed as an integet `Tensor`, should be of shape
        `batch_shape + [3]` and contain `[year, month, day]` for each date.
        Default value: `None`
      end_date: A `DateTensor` of `batch_shape`specifying the end dates for
        accrual of the last coupon in each cashflow stream. The shape of the
        input should be the same as that of `start_date`.
        Either this of `schedule` should be supplied.
        When passed as an integet `Tensor`, should be of shape
        `batch_shape + [3]` and contain `[year, month, day]` for each date.
        Default value: `None`
      discount_curve_mask: An optional integer `Tensor` of values ranging from
        `0` to `len(discount_curve_type) - 1` and of shape `batch_shape`.
        Identifies a mapping between `discount_curve_type` list and the
        underlying instruments.
        Default value: `None`.
      rate_index_curves: An instance of `RateIndexCurve` or a list of those.
        If supplied as a list and `reference_mask` is not supplid,
        the size of the list should be the same as the number of priced
        instruments. Defines the index curves for each instrument. If not
        supplied, `coupon_spec.floating_rate_type` is used to identify the
        curves.
        Default value: `None`.
      reference_mask: An optional integer `Tensor` of values ranging from
        `0` to `len(rate_index_curves) - 1` and of shape `batch_shape`.
        Identifies a mapping between `rate_index_curves` list and the underlying
        instruments.
        Default value: `None`.
      first_coupon_date: An optional `DateTensor` specifying the payment dates
        of the first coupon of the cashflow stream. Use this input for cashflows
        with irregular first coupon. Should be of the same shape as
        `start_date`.
        When passed as an integet `Tensor`, should be of shape
        `batch_shape + [3]` and contain `[year, month, day]` for each date.
        Default value: None which implies regular first coupon.
      penultimate_coupon_date: An optional `DateTensor` specifying the payment
        dates of the penultimate (next to last) coupon of the cashflow
        stream. Use this input for cashflows with irregular last coupon.
        Should be of the same shape as `end_date`.
        When passed as an integet `Tensor`, should be of shape
        `batch_shape + [3]` and contain `[year, month, day]` for each date.
        Default value: None which implies regular last coupon.
      schedule_fn: A callable that accepts `start_date`, `end_date`,
        `coupon_frequency`, `settlement_days`, `first_coupon_date`, and
        `penultimate_coupon_date` as `Tensor`s and returns coupon payment
        days.
        Default value: `None`.
      schedule: A `DateTensor` of coupon payment dates including the start and
        end dates of the cashflows.
        Default value: `None`.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: None which maps to the default dtype inferred by
        TensorFlow.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'floating_cashflow_stream'.
    """

        self._name = name or "floating_cashflow_stream"
        with tf.name_scope(self._name):
            curve_list = to_list(discount_curve_type)
            [self._discount_curve_type,
             self._mask] = process_curve_types(curve_list, discount_curve_mask)
            self._first_coupon_date = None
            self._penultimate_coupon_date = None
            if schedule is None:
                if (start_date is None) or (end_date is None):
                    raise ValueError(
                        "If `schedule` is not supplied both "
                        "`start_date` and `end_date` should be supplied")

            if schedule is None:
                if isinstance(start_date, tf.Tensor):
                    self._start_date = dateslib.dates_from_tensor(start_date)
                else:
                    self._start_date = dateslib.convert_to_date_tensor(
                        start_date)
                if isinstance(start_date, tf.Tensor):
                    self._end_date = dateslib.dates_from_tensor(end_date)
                else:
                    self._end_date = dateslib.convert_to_date_tensor(end_date)
                self._first_coupon_date = first_coupon_date
                self._penultimate_coupon_date = penultimate_coupon_date
                if self._first_coupon_date is not None:
                    if isinstance(start_date, tf.Tensor):
                        self._first_coupon_date = dateslib.dates_from_tensor(
                            first_coupon_date)
                    else:
                        self._first_coupon_date = dateslib.convert_to_date_tensor(
                            first_coupon_date)
                if self._penultimate_coupon_date is not None:
                    if isinstance(start_date, tf.Tensor):
                        self._penultimate_coupon_date = dateslib.dates_from_tensor(
                            penultimate_coupon_date)
                    else:
                        self._penultimate_coupon_date = dateslib.convert_to_date_tensor(
                            penultimate_coupon_date)
            # Ignored and weekends only is used
            calendar = dateslib.create_holiday_calendar(
                weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
            # Convert coupon and reset frequencies to PeriodTensor
            coupon_frequency = _get_attr(coupon_spec, "coupon_frequency")
            # Update coupon frequency
            if isinstance(coupon_frequency, period_pb2.Period):
                coupon_frequency = market_data_utils.get_period(
                    _get_attr(coupon_spec, "coupon_frequency"))
            if isinstance(coupon_frequency, (list, tuple)):
                coupon_frequency = market_data_utils.period_from_list(
                    *_get_attr(coupon_spec, "coupon_frequency"))
            if isinstance(coupon_frequency, dict):
                coupon_frequency = market_data_utils.period_from_dict(
                    _get_attr(coupon_spec, "coupon_frequency"))
            # Update reset frequency
            reset_frequency = _get_attr(coupon_spec, "reset_frequency")
            if isinstance(reset_frequency, period_pb2.Period):
                reset_frequency = market_data_utils.get_period(
                    _get_attr(coupon_spec, "reset_frequency"))
            if isinstance(reset_frequency, (list, tuple)):
                reset_frequency = market_data_utils.period_from_list(
                    *_get_attr(coupon_spec, "reset_frequency"))
            if isinstance(reset_frequency, dict):
                reset_frequency = market_data_utils.period_from_dict(
                    _get_attr(coupon_spec, "reset_frequency"))
            self._reset_frequency = reset_frequency
            businessday_rule = _get_attr(coupon_spec, "businessday_rule")
            roll_convention, eom = market_data_utils.get_business_day_convention(
                businessday_rule)
            notional = tf.convert_to_tensor(_get_attr(coupon_spec,
                                                      "notional_amount"),
                                            dtype=dtype,
                                            name="notional")
            self._dtype = dtype or notional.dtype

            daycount_convention = _get_attr(coupon_spec, "daycount_convention")

            daycount_fn = market_data_utils.get_daycount_fn(
                _get_attr(coupon_spec, "daycount_convention"), self._dtype)
            self._daycount_convention = daycount_convention

            self._settlement_days = tf.convert_to_tensor(
                _get_attr(coupon_spec, "settlement_days"),
                dtype=tf.int32,
                name="settlement_days")
            spread = tf.convert_to_tensor(_get_attr(coupon_spec, "spread"),
                                          dtype=self._dtype,
                                          name="spread")
            if schedule is not None:
                if isinstance(start_date, tf.Tensor):
                    coupon_dates = dateslib.dates_from_tensor(schedule)
                else:
                    coupon_dates = dateslib.convert_to_date_tensor(schedule)
                # Extract starting date for the cashflow
                self._start_date = coupon_dates[..., 0]
            elif schedule_fn is None:
                coupon_dates = _generate_schedule(
                    start_date=self._start_date,
                    end_date=self._end_date,
                    coupon_frequency=coupon_frequency,
                    roll_convention=roll_convention,
                    calendar=calendar,
                    settlement_days=self._settlement_days,
                    end_of_month=eom,
                    first_coupon_date=self._first_coupon_date,
                    penultimate_coupon_date=self._penultimate_coupon_date)
                # Extract starting date for the cashflow
                self._start_date = coupon_dates[..., 0]
            else:
                if first_coupon_date is not None:
                    first_coupon_date = self._first_coupon_date.to_tensor()
                if penultimate_coupon_date is not None:
                    penultimate_coupon_date = self._penultimate_coupon_date.to_tensor(
                    )
                    coupon_dates = schedule_fn(
                        start_date=self._start_date.to_tensor(),
                        end_date=self._end_date.to_tensor(),
                        coupon_frequency=coupon_frequency.quantity(),
                        settlement_days=self._settlement_days,
                        first_coupon_date=first_coupon_date,
                        penultimate_coupon_date=penultimate_coupon_date)
            # Convert to DateTensor if the result comes from a tf.function
            coupon_dates = dateslib.convert_to_date_tensor(coupon_dates)
            # Extract batch shape
            self._batch_shape = tf.shape(coupon_dates.ordinal())[:-1]

            accrual_start_dates = coupon_dates[..., :-1]

            coupon_start_dates = coupon_dates[..., :-1]
            coupon_end_dates = coupon_dates[..., 1:]

            accrual_end_dates = accrual_start_dates + reset_frequency.expand_dims(
                axis=-1)

            # Adjust for irregular coupons
            accrual_end_dates = dateslib.DateTensor.concat([
                coupon_end_dates[..., :1], accrual_end_dates[..., 1:-1],
                coupon_end_dates[..., -1:]
            ],
                                                           axis=-1)
            daycount_fractions = daycount_fn(start_date=coupon_start_dates,
                                             end_date=coupon_end_dates)

            self._num_cashflows = tf.shape(daycount_fractions)[-1]
            self._coupon_start_dates = coupon_start_dates
            self._coupon_end_dates = coupon_end_dates
            self._accrual_start_date = accrual_start_dates
            self._accrual_end_date = accrual_end_dates
            self._notional = notional
            self._daycount_fractions = daycount_fractions
            self._spread = spread
            self._currency = _get_attr(coupon_spec, "currency")
            self._daycount_fn = daycount_fn
            # Construct the reference curve object
            # Extract all rate_curves
            self._floating_rate_type = to_list(
                _get_attr(coupon_spec, "floating_rate_type"))
            self._currency = to_list(self._currency)
            if rate_index_curves is None:
                rate_index_curves = []
                for currency, floating_rate_type in zip(
                        self._currency, self._floating_rate_type):
                    rate_index_curves.append(
                        curve_types_lib.RateIndexCurve(
                            currency=currency, index=floating_rate_type))
            [self._reference_curve_type, self._reference_mask
             ] = process_curve_types(rate_index_curves, reference_mask)
예제 #7
0
    def __init__(self,
                 short_position: types.BoolTensor,
                 currency: types.CurrencyProtoType,
                 fixing_date: types.DateTensor,
                 fixed_rate: types.FloatTensor,
                 notional_amount: types.FloatTensor,
                 daycount_convention: types.DayCountConventionsProtoType,
                 business_day_convention: types.BusinessDayConventionProtoType,
                 calendar: types.BankHolidaysProtoType,
                 rate_term: period_pb2.Period,
                 rate_index: rate_indices.RateIndex,
                 settlement_days: Optional[types.IntTensor] = 0,
                 discount_curve_type: curve_types_lib.CurveType = None,
                 discount_curve_mask: types.IntTensor = None,
                 rate_index_curves: curve_types_lib.RateIndexCurve = None,
                 reference_mask: types.IntTensor = None,
                 config: Union[ForwardRateAgreementConfig, Dict[str,
                                                                Any]] = None,
                 batch_names: Optional[types.StringTensor] = None,
                 dtype: Optional[types.Dtype] = None,
                 name: Optional[str] = None):
        """Initializes the batch of FRA contracts.

    Args:
      short_position: Whether the contract holder lends or borrows the money.
        Default value: `True` which means that the contract holder lends the
        money at the fixed rate.
      currency: The denominated currency.
      fixing_date: A `DateTensor` specifying the dates on which forward
        rate will be fixed.
      fixed_rate: A `Tensor` of real dtype specifying the fixed rate
        payment agreed at the initiation of the individual contracts. The shape
        should be broadcastable with `fixed_rate`.
      notional_amount: A `Tensor` of real dtype broadcastable with fixed_rate
        specifying the notional amount for each contract. When the notional is
        specified as a scalar, it is assumed that all contracts have the same
        notional.
      daycount_convention: A `DayCountConvention` to determine how cashflows
        are accrued for each contract. Daycount is assumed to be the same for
        all contracts in a given batch.
      business_day_convention: A business count convention.
      calendar: A calendar to specify the weekend mask and bank holidays.
      rate_term: A tenor of the rate (usually Libor) that determines the
        floating cashflow.
      rate_index: A type of the floating leg. An instance of
        `core.rate_indices.RateIndex`.
      settlement_days: An integer `Tensor` of the shape broadcastable with the
        shape of `fixing_date`.
      discount_curve_type: An optional instance of `CurveType` or a list of
        those. If supplied as a list and `discount_curve_mask` is not supplied,
        the size of the list should be the same as the number of priced
        instruments. Defines discount curves for the instruments.
        Default value: `None`, meaning that discount curves are inferred
        from `currency` and `config`.
      discount_curve_mask: An optional integer `Tensor` of values ranging from
        `0` to `len(discount_curve_type) - 1` and of shape `batch_shape`.
        Identifies a mapping between `discount_curve_type` list and the
        underlying instruments.
        Default value: `None`.
      rate_index_curves: An instance of `RateIndexCurve` or a list of those.
        If supplied as a list and `reference_mask` is not supplid,
        the size of the list should be the same as the number of priced
        instruments. Defines the index curves for each instrument. If not
        supplied, `coupon_spec.floating_rate_type` is used to identify the
        curves.
        Default value: `None`.
      reference_mask: An optional integer `Tensor` of values ranging from
        `0` to `len(rate_index_curves) - 1` and of shape `batch_shape`.
        Identifies a mapping between `rate_index_curves` list and the underlying
        instruments.
        Default value: `None`.
      config: Optional `ForwardRateAgreementConfig` or a dictionary.
        If dictionary, then the keys should be the same as the field names of
        `ForwardRateAgreementConfig`.
      batch_names: A string `Tensor` of instrument names. Should be of shape
        `batch_shape + [2]` specying name and instrument type. This is useful
        when the `from_protos` method is used and the user needs to identify
        which instruments got batched together.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: `None` which maps to `float64`.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'forward_rate_agreement'.
    """
        self._name = name or "forward_rate_agreement"
        with tf.name_scope(self._name):
            if batch_names is not None:
                self._names = tf.convert_to_tensor(batch_names,
                                                   name="batch_names")
            else:
                self._names = None
            self._dtype = dtype or tf.float64
            ones = tf.constant(1, dtype=self._dtype)
            self._short_position = tf.where(short_position,
                                            ones,
                                            -ones,
                                            name="short_position")
            self._notional_amount = tf.convert_to_tensor(
                notional_amount, dtype=self._dtype, name="notional_amount")
            self._fixed_rate = tf.convert_to_tensor(fixed_rate,
                                                    dtype=self._dtype,
                                                    name="fixed_rate")
            settlement_days = tf.convert_to_tensor(settlement_days)
            # Business day roll convention and the end of month flag
            roll_convention, eom = market_data_utils.get_business_day_convention(
                business_day_convention)
            # TODO(b/160446193): Calendar is ignored at the moment
            calendar = dateslib.create_holiday_calendar(
                weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
            if isinstance(fixing_date, types.IntTensor):
                self._fixing_date = dateslib.dates_from_tensor(fixing_date)
            else:
                self._fixing_date = dateslib.convert_to_date_tensor(
                    fixing_date)
            self._accrual_start_date = calendar.add_business_days(
                self._fixing_date,
                settlement_days,
                roll_convention=roll_convention)

            self._day_count_fn = market_data_utils.get_daycount_fn(
                daycount_convention)
            period = rate_term
            if isinstance(rate_term, period_pb2.Period):
                period = market_data_utils.get_period(rate_term)
            if isinstance(rate_term, dict):
                period = market_data_utils.period_from_dict(rate_term)
            self._accrual_end_date = calendar.add_period_and_roll(
                self._accrual_start_date,
                period,
                roll_convention=roll_convention)
            if eom:
                self._accrual_end_date = self._accrual_end_date.to_end_of_month(
                )
            self._daycount_fractions = self._day_count_fn(
                start_date=self._accrual_start_date,
                end_date=self._accrual_end_date,
                dtype=self._dtype)
            self._settlement_days = settlement_days
            self._roll_convention = roll_convention
            # Get discount and reference curves
            self._currency = cashflow_streams.to_list(currency)
            self._rate_index = cashflow_streams.to_list(rate_index)
            # Get a mask for the reference curves
            if rate_index_curves is None:
                rate_index_curves = []
                if len(self._currency) != len(self._rate_index):
                    raise ValueError(
                        "When rate_index_curves` is not supplied, number of currencies "
                        "and rate indices should be the same `but it is {0} and "
                        "{1}".format(len(self._currency),
                                     len(self._rate_index)))

                for currency, rate_index in zip(self._currency,
                                                self._rate_index):
                    rate_index_curves.append(
                        curve_types_lib.RateIndexCurve(currency=currency,
                                                       index=rate_index))
            [self._reference_curve_type, self._reference_mask
             ] = cashflow_streams.process_curve_types(rate_index_curves,
                                                      reference_mask)
            # Get a mask for the discount curves
            self._config = _process_config(config)
            if discount_curve_type is None:
                curve_list = []
                for currency in self._currency:
                    if currency in self._config.discounting_curve:
                        discount_curve_type = self._config.discounting_curve[
                            currency]
                    else:
                        # Default discounting is the risk free curve
                        discount_curve_type = curve_types_lib.RiskFreeCurve(
                            currency=currency)
                    curve_list.append(discount_curve_type)
            else:
                curve_list = cashflow_streams.to_list(discount_curve_type)

            # Get masks for discount and reference curves
            [self._discount_curve_type, self._mask
             ] = cashflow_streams.process_curve_types(curve_list,
                                                      discount_curve_mask)

            # Get batch shape
            self._batch_shape = self._daycount_fractions.shape.as_list()[:-1]
예제 #8
0
    def __init__(self,
                 maturity_dates: types.DateTensor,
                 discount_factors: tf.Tensor,
                 valuation_date: types.DateTensor,
                 interpolator: Optional[_InterpolationMethod] = None,
                 interpolate_rates: Optional[bool] = True,
                 daycount_convention: Optional[
                     _DayCountConventionsProtoType] = None,
                 curve_type: Optional[curve_types.CurveType] = None,
                 dtype: Optional[tf.DType] = None,
                 name: Optional[str] = None):
        """Initializes the interest rate curve.

    Args:
      maturity_dates: A `DateTensor` containing the maturity dates on which the
        curve is specified.
      discount_factors: A `Tensor` of real dtype specifying the discount factors
        corresponding to the input maturities. The shape of this input should
        match the shape of `maturity_dates`.
      valuation_date: A scalar `DateTensor` specifying the valuation (or
        settlement) date for the curve.
      interpolator: An instance of `InterpolationMethod`.
        Default value: `None` in which case cubic interpolation is used.
      interpolate_rates: A boolean specifying whether the interpolation should
        be done in discount rates or discount factors space.
        Default value: `True`, i.e., interpolation is done in the discount
        factors space.
      daycount_convention: `DayCountConventions` to use for the interpolation
        purpose.
        Default value: `None` which maps to actual/365 day count convention.
      curve_type: An instance of `CurveTypes` to mark the rate curve.
        Default value: `None` which means that the curve does not have the
          marker.
      dtype: `tf.Dtype`. Optional input specifying the dtype of the `rates`
        input.
      name: Python str. The name to give to the ops created by this function.
        Default value: `None` which maps to 'rate_curve'.
    """
        self._name = name or "rate_curve"
        with tf.compat.v1.name_scope(self._name):
            self._discount_factor_nodes = tf.convert_to_tensor(
                discount_factors, dtype=dtype, name="curve_discount_factors")
            self._dtype = dtype or self._discount_factor_nodes.dtype
            if interpolator is None or interpolator == _InterpolationMethod.CUBIC:

                def cubic_interpolator(xi, x, y):
                    spline_coeffs = math.interpolation.cubic.build_spline(x, y)
                    return math.interpolation.cubic.interpolate(xi,
                                                                spline_coeffs,
                                                                dtype=dtype)

                interpolator = cubic_interpolator
                self._interpolation_method = _InterpolationMethod.CUBIC
            elif interpolator == _InterpolationMethod.LINEAR:

                def linear_interpolator(xi, x, y):
                    return math.interpolation.linear.interpolate(xi,
                                                                 x,
                                                                 y,
                                                                 dtype=dtype)

                interpolator = linear_interpolator
                self._interpolation_method = _InterpolationMethod.LINEAR
            elif interpolator == _InterpolationMethod.CONSTANT_FORWARD:

                def constant_fwd(xi, x, y):
                    return rates_lib.constant_fwd.interpolate(xi,
                                                              x,
                                                              y,
                                                              dtype=dtype)

                interpolator = constant_fwd
                self._interpolation_method = _InterpolationMethod.CONSTANT_FORWARD
            else:
                raise ValueError(
                    f"Unknown interpolation method {interpolator}.")
            self._dates = dateslib.convert_to_date_tensor(maturity_dates)
            self._valuation_date = dateslib.convert_to_date_tensor(
                valuation_date)

            self._daycount_convention = (daycount_convention
                                         or _DayCountConventions.ACTUAL_365)
            self._day_count_fn = utils.get_daycount_fn(
                self._daycount_convention)
            self._times = self._get_time(self._dates)
            self._interpolator = interpolator
            self._interpolate_rates = interpolate_rates
            # Precompute discount rates:
            self._curve_type = curve_type
예제 #9
0
    def __init__(self,
                 start_date: types.DateTensor,
                 end_date: types.DateTensor,
                 coupon_spec: coupon_specs.FixedCouponSpecs,
                 discount_curve_type: _CurveType,
                 first_coupon_date: Optional[types.DateTensor] = None,
                 penultimate_coupon_date: Optional[types.DateTensor] = None,
                 dtype: Optional[types.Dtype] = None,
                 name: Optional[str] = None):
        """Initializes a batch of fixed cashflow streams.

    Args:
      start_date: A `DateTensor` of `batch_shape` specifying the starting dates
        of the accrual of the first coupon of the cashflow stream. The shape of
        the input correspond to the number of streams being created.
      end_date: A `DateTensor` of `batch_shape`specifying the end dates for
        accrual of the last coupon in each cashflow stream. The shape of the
        input should be the same as that of `start_date`.
      coupon_spec: An instance of `FixedCouponSpecs` specifying the
        details of the coupon payment for the cashflow stream.
      discount_curve_type: An instance of `CurveType`.
      first_coupon_date: An optional `DateTensor` specifying the payment dates
        of the first coupon of the cashflow stream. Use this input for cashflows
        with irregular first coupon. Should be of the same shape as
        `start_date`.
        Default value: None which implies regular first coupon.
      penultimate_coupon_date: An optional `DateTensor` specifying the payment
        dates of the penultimate (next to last) coupon of the cashflow
        stream. Use this input for cashflows with irregular last coupon.
        Should be of the same shape as `end_date`.
        Default value: None which implies regular last coupon.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: None which maps to the default dtype inferred by
        TensorFlow.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'fixed_cashflow_stream'.
    """
        self._name = name or "fixed_cashflow_stream"

        with tf.name_scope(self._name):
            curve_list = to_list(discount_curve_type)
            [self._discount_curve_type,
             self._mask] = process_curve_types(curve_list)
            self._start_date = dateslib.convert_to_date_tensor(start_date)
            self._end_date = dateslib.convert_to_date_tensor(end_date)
            self._first_coupon_date = first_coupon_date
            self._penultimate_coupon_date = penultimate_coupon_date
            if self._first_coupon_date is not None:
                self._first_coupon_date = dateslib.convert_to_date_tensor(
                    first_coupon_date)
            if self._penultimate_coupon_date is not None:
                self._penultimate_coupon_date = dateslib.convert_to_date_tensor(
                    penultimate_coupon_date)

            coupon_frequency = coupon_spec.coupon_frequency
            if isinstance(coupon_frequency, (period_pb2.Period, list, tuple)):
                coupon_frequency = market_data_utils.get_period(
                    coupon_spec.coupon_frequency)

            businessday_rule = coupon_spec.businessday_rule
            # Business day roll convention and the end of month flag
            roll_convention, eom = market_data_utils.get_business_day_convention(
                businessday_rule)

            notional = tf.convert_to_tensor(coupon_spec.notional_amount,
                                            dtype=dtype,
                                            name="notional")
            self._dtype = dtype or notional.dtype
            fixed_rate = tf.convert_to_tensor(coupon_spec.fixed_rate,
                                              dtype=self._dtype,
                                              name="fixed_rate")
            # TODO(b/160446193): Calendar is ignored and weekends only is used
            calendar = dateslib.create_holiday_calendar(
                weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
            daycount_fn = market_data_utils.get_daycount_fn(
                coupon_spec.daycount_convention)

            self._settlement_days = tf.convert_to_tensor(
                coupon_spec.settlement_days,
                dtype=tf.int32,
                name="settlement_days")

            coupon_dates = _generate_schedule(
                start_date=self._start_date,
                end_date=self._end_date,
                coupon_frequency=coupon_frequency,
                roll_convention=roll_convention,
                calendar=calendar,
                settlement_days=self._settlement_days,
                end_of_month=eom,
                first_coupon_date=self._first_coupon_date,
                penultimate_coupon_date=self._penultimate_coupon_date)

            self._batch_shape = coupon_dates.shape.as_list()[:-1]
            payment_dates = coupon_dates[..., 1:]

            daycount_fractions = daycount_fn(start_date=coupon_dates[..., :-1],
                                             end_date=coupon_dates[..., 1:],
                                             dtype=self._dtype)

            coupon_rate = tf.expand_dims(fixed_rate, axis=-1)

            self._num_cashflows = payment_dates.shape.as_list()[-1]
            self._payment_dates = payment_dates
            self._notional = notional
            self._daycount_fractions = daycount_fractions
            self._coupon_rate = coupon_rate
            self._calendar = coupon_rate
            self._fixed_rate = tf.convert_to_tensor(fixed_rate,
                                                    dtype=self._dtype)
            self._daycount_fn = daycount_fn
예제 #10
0
    def __init__(self,
                 start_date: types.DateTensor,
                 end_date: types.DateTensor,
                 coupon_spec: coupon_specs.FloatCouponSpecs,
                 discount_curve_type: _CurveType,
                 first_coupon_date: Optional[types.DateTensor] = None,
                 penultimate_coupon_date: Optional[types.DateTensor] = None,
                 dtype: Optional[types.Dtype] = None,
                 name: Optional[str] = None):
        """Initializes a batch of floating cashflow streams.

    Args:
      start_date: A `DateTensor` of `batch_shape` specifying the starting dates
        of the accrual of the first coupon of the cashflow stream. The shape of
        the input correspond to the number of streams being created.
      end_date: A `DateTensor` of `batch_shape`specifying the end dates for
        accrual of the last coupon in each cashflow stream. The shape of the
        input should be the same as that of `start_date`.
      coupon_spec: An instance of `FloatCouponSpecs` specifying the
        details of the coupon payment for the cashflow stream.
      discount_curve_type: An instance of `CurveType`.
      first_coupon_date: An optional `DateTensor` specifying the payment dates
        of the first coupon of the cashflow stream. Use this input for cashflows
        with irregular first coupon. Should be of the same shape as
        `start_date`.
        Default value: None which implies regular first coupon.
      penultimate_coupon_date: An optional `DateTensor` specifying the payment
        dates of the penultimate (next to last) coupon of the cashflow
        stream. Use this input for cashflows with irregular last coupon.
        Should be of the same shape as `end_date`.
        Default value: None which implies regular last coupon.
      dtype: `tf.Dtype` of the input and output real `Tensor`s.
        Default value: None which maps to the default dtype inferred by
        TensorFlow.
      name: Python str. The name to give to the ops created by this class.
        Default value: `None` which maps to 'floating_cashflow_stream'.
    """

        self._name = name or "floating_cashflow_stream"
        with tf.name_scope(self._name):
            curve_list = to_list(discount_curve_type)
            [self._discount_curve_type,
             self._mask] = process_curve_types(curve_list)
            self._first_coupon_date = None
            self._penultimate_coupon_date = None
            self._start_date = dateslib.convert_to_date_tensor(start_date)
            self._end_date = dateslib.convert_to_date_tensor(end_date)
            if self._first_coupon_date is not None:
                self._first_coupon_date = dateslib.convert_to_date_tensor(
                    first_coupon_date)
            if self._penultimate_coupon_date is not None:
                self._penultimate_coupon_date = dateslib.convert_to_date_tensor(
                    penultimate_coupon_date)
            # Ignored and weekends only is used
            calendar = dateslib.create_holiday_calendar(
                weekend_mask=dateslib.WeekendMask.SATURDAY_SUNDAY)
            # Convert coupon and reset frequencies to PeriodTensor
            coupon_frequency = coupon_spec.coupon_frequency
            if isinstance(coupon_frequency, (period_pb2.Period, list, tuple)):
                coupon_frequency = market_data_utils.get_period(
                    coupon_spec.coupon_frequency)
            reset_frequency = coupon_spec.reset_frequency
            if isinstance(reset_frequency, (period_pb2.Period, list, tuple)):
                reset_frequency = market_data_utils.get_period(
                    coupon_spec.reset_frequency)
            self._reset_frequency = reset_frequency
            businessday_rule = coupon_spec.businessday_rule
            roll_convention, eom = market_data_utils.get_business_day_convention(
                businessday_rule)
            notional = tf.convert_to_tensor(coupon_spec.notional_amount,
                                            dtype=dtype,
                                            name="notional")
            self._dtype = dtype or notional.dtype

            daycount_convention = coupon_spec.daycount_convention
            daycount_fn = market_data_utils.get_daycount_fn(
                coupon_spec.daycount_convention)
            self._daycount_convention = daycount_convention

            self._settlement_days = tf.convert_to_tensor(
                coupon_spec.settlement_days,
                dtype=tf.int32,
                name="settlement_days")
            spread = tf.convert_to_tensor(coupon_spec.spread,
                                          dtype=self._dtype,
                                          name="spread")

            coupon_dates = _generate_schedule(
                start_date=self._start_date,
                end_date=self._end_date,
                coupon_frequency=coupon_frequency,
                roll_convention=roll_convention,
                calendar=calendar,
                settlement_days=self._settlement_days,
                end_of_month=eom,
                first_coupon_date=self._first_coupon_date,
                penultimate_coupon_date=self._penultimate_coupon_date)
            # Extract batch shape
            self._batch_shape = coupon_dates.shape.as_list()[:-1]

            accrual_start_dates = coupon_dates[..., :-1]

            coupon_start_dates = coupon_dates[..., :-1]
            coupon_end_dates = coupon_dates[..., 1:]

            accrual_end_dates = accrual_start_dates + reset_frequency.expand_dims(
                axis=-1)

            # Adjust for irregular coupons
            accrual_end_dates = dateslib.DateTensor.concat([
                coupon_end_dates[..., :1], accrual_end_dates[..., 1:-1],
                coupon_end_dates[..., -1:]
            ],
                                                           axis=-1)
            daycount_fractions = daycount_fn(start_date=coupon_start_dates,
                                             end_date=coupon_end_dates,
                                             dtype=self._dtype)

            self._num_cashflows = daycount_fractions.shape.as_list()[-1]
            self._coupon_start_dates = coupon_start_dates
            self._coupon_end_dates = coupon_end_dates
            self._accrual_start_date = accrual_start_dates
            self._accrual_end_date = accrual_end_dates
            self._notional = notional
            self._daycount_fractions = daycount_fractions
            self._spread = spread
            self._currency = coupon_spec.currency
            self._daycount_fn = daycount_fn
            # Construct the reference curve object
            # Extract all rate_curves
            self._floating_rate_type = to_list(coupon_spec.floating_rate_type)
            self._currency = to_list(self._currency)
            rate_index_curves = []
            for currency, floating_rate_type in zip(self._currency,
                                                    self._floating_rate_type):
                rate_index_curves.append(
                    curve_types_lib.RateIndexCurve(currency=currency,
                                                   index=floating_rate_type))
            [self._reference_curve_type,
             self._reference_mask] = process_curve_types(rate_index_curves)
예제 #11
0
    def from_volatility_surface(cls,
                                implied_volatility_surface,
                                variance_process,
                                initial_spot,
                                initial_variance,
                                rho=None,
                                risk_free_rate=None,
                                dividend_yield=None,
                                time_step=None,
                                num_grid_points=None,
                                grid_minimums=None,
                                grid_maximums=None,
                                dtype=None):
        """Creates a `LocalStochasticVolatilityModel` from volatility surface.

    This function computes the leverage function for the LSV model by first
    computing the joint probablity density function `p(t, X(t), v(t))` where
    `X(t)` is the log of the spot price and `v(t)` is the variance at time `t`.
    The joint probablity density is computed using the Fokker-Planck equation of
    the LSV model (see 6.8.2 in Ref [1]):
    ```None
    dp/dt = 1/2 d^2 [v L(t,X)^2 p]/dX^2 + 1/2 d^2 [b(v)^2 p]/dv^2 +
            rho d^2 [sqrt(v)L(t,X)b(v) p]/dXdv -
            d[(r - d - 1/2 v L(t,X)^2)p]/dX -
            d[a(v) p]/dv
    ```

    where `a(v)` and `b(v)` are the drift and diffusion functions for the
    variance process. Defining

    ```None
    I_n(k,t) = int v^n p(t, k, v) dv
    ```

    we can calculate the leverage function as follows:
    ```None
    L(k, t) = sigma(exp(k), t) sqrt(I_0(k, t)/I_1(k, t)).
    ```

    Args:
      implied_volatility_surface: Either an instance of
        `processed_market_data.VolatilitySurface` or a Python object containing
        the implied volatility market data. If the input is a Python object,
        then the object must implement a function `volatility(strike,
        expiry_times)` which takes real `Tensor`s corresponding to option
        strikes and time to expiry and returns a real `Tensor` containing the
        corresponding market implied volatility.
      variance_process: An instance of `LSVVarianceModel` or
        `ItoProcess`specifying the dynamics of the variance process of
        the LSV model.
      initial_spot: A real scalar `Tensor` specifying the underlying spot price
        on the valuation date.
      initial_variance: A real scalar `Tensor` specifying the initial variance
        on the valuation date.
      rho: A real scalar `Tensor` specifying the correlation between spot price
        and the stochastic variance.
      risk_free_rate: A real scalar `Tensor` specifying the (continuosly
        compounded) risk free interest rate. If the underlying is an FX rate,
        then use this input to specify the domestic interest rate.
        Default value: `None` in which case the input is set to zero.
      dividend_yield: A real scalar `Tensor` specifying the (continuosly
        compounded) divident yield. If the underlying is an FX rate, then use
        this input to specify the foreign interest rate.
        Default value: `None` in which case the input is set to zero.
      time_step: An optional real scalar `Tensor` specifying the time step
        during the numerical solution of the Fokker-Planck PDE.
        Default value: None, in which case `time_step` corresponding to 100 time
          steps is used.
      num_grid_points: A scalar integer `Tensor` specifying the number of
        discretization points for each spatial dimension.
        Default value: None, in which case number of grid points is set to 100.
      grid_minimums: An optional `Tensor` of size 2 containing the minimum grid
        points for PDE spatial discretization. `grid_minimums[0]` correspond
        to the minimum spot price in the spatial grid and `grid_minimums[1]`
        correspond to the minimum variance value.
      grid_maximums: An optional `Tensor` of size 2 containing the maximum grid
        points for PDE spatial discretization. `grid_maximums[0]` correspond
        to the maximum spot price in the spatial grid and `grid_maximums[1]`
        correspond to the maximum variance value.
      dtype: The default dtype to use when converting values to `Tensor`s.
        Default value: `None` which means that default dtypes inferred by
          TensorFlow are used.

    Returns:
      An instance of `LocalStochasticVolatilityModel` constructed using the
      input data.
    """

        if risk_free_rate is None:
            discount_factor_fn = lambda t: tf.ones_like(t, dtype=dtype)
        else:
            r = tf.convert_to_tensor(risk_free_rate, dtype=dtype)
            discount_factor_fn = lambda t: tf.math.exp(-r * t)
        lv_model = lvm.LocalVolatilityModel.from_volatility_surface(
            dim=1,
            spot=initial_spot,
            implied_volatility_surface=implied_volatility_surface,
            discount_factor_fn=discount_factor_fn,
            dividend_yield=dividend_yield,
            dtype=dtype)

        dtype = dtype or lv_model.dtype()
        day_count_fn = utils.get_daycount_fn(
            implied_volatility_surface.daycount_convention)
        max_time = tf.math.reduce_max(
            day_count_fn(
                start_date=implied_volatility_surface.settlement_date(),
                end_date=implied_volatility_surface.node_expiries()))
        if time_step is None:
            time_step = max_time / 100.0

        rho = rho or 0.0
        num_grid_points = num_grid_points or 100

        leverage_fn = _leverage_function_using_pde(
            risk_free_rate=risk_free_rate,
            dividend_yield=dividend_yield,
            lv_model=lv_model,
            variance_model=variance_process,
            rho=[rho],
            initial_spot=initial_spot,
            initial_variance=initial_variance,
            time_step=time_step,
            max_time=max_time,
            num_grid_points=num_grid_points,
            grid_minimums=grid_minimums,
            grid_maximums=grid_maximums,
            dtype=dtype)
        return LocalStochasticVolatilityModel(leverage_fn,
                                              variance_process,
                                              risk_free_rate=risk_free_rate,
                                              dividend_yield=dividend_yield,
                                              rho=rho,
                                              dtype=dtype)