Esempio n. 1
0
def bond_option_price(*,
                      strikes,
                      expiries,
                      maturities,
                      discount_rate_fn,
                      dim,
                      mean_reversion,
                      volatility,
                      corr_matrix=None,
                      is_call_options=True,
                      num_samples=1,
                      random_type=None,
                      seed=None,
                      skip=0,
                      time_step=None,
                      dtype=None,
                      name=None):
  """Calculates European bond option prices using the HJM model.

  Bond options are fixed income securities which give the holder a right to
  exchange at a future date (the option expiry) a zero coupon bond for a fixed
  price (the strike of the option). The maturity date of the bond is after the
  the expiry of the option. If `P(t,T)` denotes the price at time `t` of a zero
  coupon bond with maturity `T`, then the payoff from the option at option
  expiry, `T0`, is given by:

  ```None
  payoff = max(P(T0, T) - X, 0)
  ```
  where `X` is the strike price of the option.

  #### Example

  ````python
  import numpy as np
  import tensorflow.compat.v2 as tf
  import tf_quant_finance as tff

  dtype = tf.float64

  discount_rate_fn = lambda x: 0.01 * tf.ones_like(x, dtype=dtype)
  expiries = np.array([1.0])
  maturities = np.array([5.0])
  strikes = np.exp(-0.01 * maturities) / np.exp(-0.01 * expiries)
  price = tff.models.hjm.bond_option_price(
      strikes=strikes,
      expiries=expiries,
      maturities=maturities,
      dim=1,
      mean_reversion=[0.03],
      volatility=[0.02],
      discount_rate_fn=discount_rate_fn,
      dtype=dtype)
  # Expected value: [[0.02817777]]
  ````

  Args:
    strikes: A real `Tensor` of any shape and dtype. The strike price of the
      options. The shape of this input determines the number (and shape) of the
      options to be priced and the output.
    expiries: A real `Tensor` of the same dtype and compatible shape as
      `strikes`.  The time to expiry of each bond option.
    maturities: A real `Tensor` of the same dtype and compatible shape as
      `strikes`.  The time to maturity of the underlying zero coupon bonds.
    discount_rate_fn: A Python callable that accepts expiry time as a real
      `Tensor` and returns a `Tensor` of shape `input_shape`. Computes the
      zero coupon bond yield at the present time for the input expiry time.
    dim: A Python scalar which corresponds to the number of factors within a
      single HJM model.
    mean_reversion: A real positive `Tensor` of shape `[dim]`. Corresponds to
      the mean reversion rate of each factor.
    volatility: A real positive `Tensor` of the same `dtype` and shape as
      `mean_reversion` or a callable with the following properties: (a)  The
        callable should accept a scalar `Tensor` `t` and a 1-D `Tensor` `r(t)`
        of shape `[num_samples]` and returns a 2-D `Tensor` of shape
        `[num_samples, dim]`. The variable `t`  stands for time and `r(t)` is
        the short rate at time `t`.  The function returns instantaneous
        volatility `sigma(t) = sigma(t, r(t))`. When `volatility` is specified
        is a real `Tensor`, each factor is assumed to have a constant
        instantaneous volatility  and the  model is effectively a Gaussian HJM
        model. Corresponds to the instantaneous volatility of each factor.
    corr_matrix: A `Tensor` of shape `[dim, dim]` and the same `dtype` as
      `mean_reversion`. Corresponds to the correlation matrix `Rho`.
      Default value: None, meaning the factors are uncorrelated.
    is_call_options: A boolean `Tensor` of a shape compatible with `strikes`.
      Indicates whether the option is a call (if True) or a put (if False). If
      not supplied, call options are assumed.
    num_samples: Positive scalar `int32` `Tensor`. The number of simulation
      paths during Monte-Carlo valuation.
      Default value: The default value is 1.
    random_type: Enum value of `RandomType`. The type of (quasi)-random number
      generator to use to generate the simulation paths.
      Default value: `None` which maps to the standard pseudo-random numbers.
    seed: Seed for the random number generator. The seed is only relevant if
      `random_type` is one of `[STATELESS, PSEUDO, HALTON_RANDOMIZED,
      PSEUDO_ANTITHETIC, STATELESS_ANTITHETIC]`. For `PSEUDO`,
      `PSEUDO_ANTITHETIC` and `HALTON_RANDOMIZED` the seed should be an Python
      integer. For `STATELESS` and  `STATELESS_ANTITHETIC `must be supplied as
      an integer `Tensor` of shape `[2]`.
      Default value: `None` which means no seed is set.
    skip: `int32` 0-d `Tensor`. The number of initial points of the Sobol or
      Halton sequence to skip. Used only when `random_type` is 'SOBOL',
      'HALTON', or 'HALTON_RANDOMIZED', otherwise ignored.
      Default value: `0`.
    time_step: Scalar real `Tensor`. Maximal distance between time grid points
      in Euler scheme. Relevant when Euler scheme is used for simulation.
      Default value: `None`.
    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.
    name: Python string. The name to give to the ops created by this class.
      Default value: `None` which maps to the default name
        `hw_bond_option_price`.

  Returns:
    A `Tensor` of real dtype and shape  `strikes.shape + [1]` containing the
    computed option prices.
  """
  if time_step is None:
    raise ValueError('`time_step` must be provided for simulation based '
                     'bond option valuation.')

  name = name or 'hjm_bond_option_price'
  if dtype is None:
    dtype = tf.convert_to_tensor([0.0]).dtype
  with tf.name_scope(name):
    strikes = tf.convert_to_tensor(strikes, dtype=dtype, name='strikes')
    expiries = tf.convert_to_tensor(expiries, dtype=dtype, name='expiries')
    maturities = tf.convert_to_tensor(
        maturities, dtype=dtype, name='maturities')
    is_call_options = tf.convert_to_tensor(
        is_call_options, dtype=tf.bool, name='is_call_options')
    model = quasi_gaussian_hjm.QuasiGaussianHJM(
        dim,
        mean_reversion=mean_reversion,
        volatility=volatility,
        initial_discount_rate_fn=discount_rate_fn,
        corr_matrix=corr_matrix,
        dtype=dtype)

    def sample_discount_curve_paths_fn(times, curve_times, num_samples):
      p_t_tau, r_t, _ = model.sample_discount_curve_paths(
          times=times,
          curve_times=curve_times,
          num_samples=num_samples,
          random_type=random_type,
          time_step=time_step,
          seed=seed,
          skip=skip)
      p_t_tau = tf.expand_dims(p_t_tau, axis=-1)
      r_t = tf.expand_dims(r_t, axis=-1)
      return p_t_tau, r_t

    return zero_coupon_bond_option_util.options_price_from_samples(
        strikes,
        expiries,
        maturities,
        is_call_options,
        sample_discount_curve_paths_fn,
        num_samples,
        time_step,
        dtype=dtype)
Esempio n. 2
0
def price(*,
          expiries,
          fixed_leg_payment_times,
          fixed_leg_daycount_fractions,
          fixed_leg_coupon,
          reference_rate_fn,
          num_hjm_factors,
          mean_reversion,
          volatility,
          time_step,
          notional=None,
          is_payer_swaption=None,
          num_samples=1,
          random_type=None,
          seed=None,
          skip=0,
          dtype=None,
          name=None):
    """Calculates the price of European swaptions using the HJM model.

  A European Swaption is a contract that gives the holder an option to enter a
  swap contract at a future date at a prespecified fixed rate. A swaption that
  grants the holder the right to pay fixed rate and receive floating rate is
  called a payer swaption while the swaption that grants the holder the right to
  receive fixed and pay floating payments is called the receiver swaption.
  Typically the start date (or the inception date) of the swap coincides with
  the expiry of the swaption. Mid-curve swaptions are currently not supported
  (b/160061740).

  This implementation uses the HJM model to numerically value the swaption via
  Monte-Carlo. For more information on the formulation of the HJM model, see
  quasi_gaussian_hjm.py.


  #### References:
    [1]: D. Brigo, F. Mercurio. Interest Rate Models-Theory and Practice.
    Second Edition. 2007. Section 6.7, page 237.

  Args:
    expiries: A real `Tensor` of any shape and dtype. The time to expiration of
      the swaptions. The shape of this input determines the number (and shape)
      of swaptions to be priced and the shape of the output.
    fixed_leg_payment_times: A real `Tensor` of the same dtype as `expiries`.
      The payment times for each payment in the fixed leg. The shape of this
      input should be `expiries.shape + [n]` where `n` denotes the number of
      fixed payments in each leg. The `fixed_leg_payment_times` should be
      greater-than or equal-to the corresponding expiries.
    fixed_leg_daycount_fractions: A real `Tensor` of the same dtype and
      compatible shape as `fixed_leg_payment_times`. The daycount fractions for
      each payment in the fixed leg.
    fixed_leg_coupon: A real `Tensor` of the same dtype and compatible shape as
      `fixed_leg_payment_times`. The fixed rate for each payment in the fixed
      leg.
    reference_rate_fn: A Python callable that accepts expiry time as a real
      `Tensor` and returns a `Tensor` of shape `input_shape +
      [num_hjm_factors]`. Returns the continuously compounded zero rate at the
      present time for the input expiry time.
    num_hjm_factors: A Python scalar which corresponds to the number of factors
      in the HJM model to be used for pricing.
    mean_reversion: A real positive `Tensor` of shape `[num_hjm_factors]`.
      Corresponds to the mean reversion rate of each factor.
    volatility: A real positive `Tensor` of the same `dtype` and shape as
      `mean_reversion` or a callable with the following properties: (a)  The
        callable should accept a scalar `Tensor` `t` and a 1-D `Tensor` `r(t)`
        of shape `[num_samples]` and returns a 2-D `Tensor` of shape
        `[num_samples, num_hjm_factors]`. The variable `t`  stands for time and
        `r(t)` is the short rate at time `t`.  The function returns the
        instantaneous volatility `sigma(t) = sigma(t, r(r))`. When `volatility`
        is specified as a real `Tensor`, each factor is assumed to have a
        constant instantaneous volatility  and the  model is effectively a
        Gaussian HJM model. Corresponds to the instantaneous volatility of each
        factor.
    time_step: Scalar real `Tensor`. Maximal distance between time grid points
      in Euler scheme. Relevant when Euler scheme is used for simulation. This
      input is required.
    notional: An optional `Tensor` of same dtype and compatible shape as
      `strikes`specifying the notional amount for the underlying swaps.
       Default value: None in which case the notional is set to 1.
    is_payer_swaption: A boolean `Tensor` of a shape compatible with `expiries`.
      Indicates whether the swaption is a payer (if True) or a receiver (if
      False) swaption. If not supplied, payer swaptions are assumed.
    num_samples: Positive scalar `int32` `Tensor`. The number of simulation
      paths during Monte-Carlo valuation. This input is ignored during analytic
      valuation.
      Default value: The default value is 1.
    random_type: Enum value of `RandomType`. The type of (quasi)-random number
      generator to use to generate the simulation paths. This input is relevant
      only for Monte-Carlo valuation and ignored during analytic valuation.
      Default value: `None` which maps to the standard pseudo-random numbers.
    seed: Seed for the random number generator. The seed is only relevant if
      `random_type` is one of `[STATELESS, PSEUDO, HALTON_RANDOMIZED,
      PSEUDO_ANTITHETIC, STATELESS_ANTITHETIC]`. For `PSEUDO`,
      `PSEUDO_ANTITHETIC` and `HALTON_RANDOMIZED` the seed should be an Python
      integer. For `STATELESS` and  `STATELESS_ANTITHETIC` must be supplied as
      an integer `Tensor` of shape `[2]`. This input is relevant only for
      Monte-Carlo valuation and ignored during analytic valuation.
      Default value: `None` which means no seed is set.
    skip: `int32` 0-d `Tensor`. The number of initial points of the Sobol or
      Halton sequence to skip. Used only when `random_type` is 'SOBOL',
      'HALTON', or 'HALTON_RANDOMIZED', otherwise ignored.
      Default value: `0`.
    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.
    name: Python string. The name to give to the ops created by this function.
      Default value: `None` which maps to the default name `hjm_swaption_price`.

  Returns:
    A `Tensor` of real dtype and shape expiries.shape + [num_hjm_factors]
    containing the computed swaption prices. For swaptions that have reset in
    the past (expiries<0), the function sets the corresponding option prices to
    0.0.
  """
    if time_step is None:
        raise ValueError('`time_step` must be provided for simulation based '
                         'swaption valuation.')

    # TODO(b/160061740): Extend the functionality to support mid-curve swaptions.
    name = name or 'hjm_swaption_price'
    with tf.name_scope(name):
        expiries = tf.convert_to_tensor(expiries, dtype=dtype, name='expiries')
        dtype = dtype or expiries.dtype
        fixed_leg_payment_times = tf.convert_to_tensor(
            fixed_leg_payment_times,
            dtype=dtype,
            name='fixed_leg_payment_times')
        fixed_leg_daycount_fractions = tf.convert_to_tensor(
            fixed_leg_daycount_fractions,
            dtype=dtype,
            name='fixed_leg_daycount_fractions')
        fixed_leg_coupon = tf.convert_to_tensor(fixed_leg_coupon,
                                                dtype=dtype,
                                                name='fixed_leg_coupon')
        notional = tf.convert_to_tensor(notional, dtype=dtype, name='notional')
        notional = tf.expand_dims(tf.broadcast_to(notional, expiries.shape),
                                  axis=-1)
        if is_payer_swaption is None:
            is_payer_swaption = True
        is_payer_swaption = tf.convert_to_tensor(is_payer_swaption,
                                                 dtype=tf.bool,
                                                 name='is_payer_swaption')

        output_shape = expiries.shape.as_list() + [1]
        # Add a dimension corresponding to multiple cashflows in a swap
        if expiries.shape.rank == fixed_leg_payment_times.shape.rank - 1:
            expiries = tf.expand_dims(expiries, axis=-1)
        elif expiries.shape.rank < fixed_leg_payment_times.shape.rank - 1:
            raise ValueError(
                'Swaption expiries not specified for all swaptions '
                'in the batch. Expected rank {} but received {}.'.format(
                    fixed_leg_payment_times.shape.rank - 1,
                    expiries.shape.rank))

        # Expected shape: batch_shape + [m], where m is the number of fixed leg
        # payments per underlying swap. This is the same as
        # fixed_leg_payment_times.shape
        #
        # We need to explicitly use tf.repeat because we need to price
        # batch_shape + [m] bond options with different strikes along the last
        # dimension.
        expiries = tf.repeat(expiries,
                             tf.shape(fixed_leg_payment_times)[-1],
                             axis=-1)

        # Monte-Carlo pricing
        model = quasi_gaussian_hjm.QuasiGaussianHJM(
            num_hjm_factors,
            mean_reversion=mean_reversion,
            volatility=volatility,
            initial_discount_rate_fn=reference_rate_fn,
            dtype=dtype)

        def _sample_discount_curve_path_fn(times, curve_times, num_samples):
            p_t_tau, r_t, _ = model.sample_discount_curve_paths(
                times=times,
                curve_times=curve_times,
                num_samples=num_samples,
                random_type=random_type,
                time_step=time_step,
                seed=seed,
                skip=skip)
            p_t_tau = tf.expand_dims(p_t_tau, axis=-1)
            r_t = tf.expand_dims(r_t, axis=-1)
            return p_t_tau, r_t

        payoff_discount_factors, payoff_bond_price = (
            swaption_util.discount_factors_and_bond_prices_from_samples(
                expiries=expiries,
                payment_times=fixed_leg_payment_times,
                sample_discount_curve_paths_fn=_sample_discount_curve_path_fn,
                num_samples=num_samples,
                time_step=time_step,
                dtype=dtype))

        # Add an axis corresponding to `dim`
        fixed_leg_pv = tf.expand_dims(
            fixed_leg_coupon * fixed_leg_daycount_fractions,
            axis=-1) * payoff_bond_price

        # Sum fixed coupon payments within each swap.
        # Here, axis=-2 is the payments axis - i.e. summing over all payments; and
        # the last axis is the `dim` axis, as explained in comment above
        # `fixed_leg_pv` (Note that for HJM the dim of this axis is 1 always).
        fixed_leg_pv = tf.math.reduce_sum(fixed_leg_pv, axis=-2)
        float_leg_pv = 1.0 - payoff_bond_price[..., -1, :]
        payoff_swap = payoff_discount_factors[..., -1, :] * (float_leg_pv -
                                                             fixed_leg_pv)
        payoff_swap = tf.where(is_payer_swaption, payoff_swap,
                               -1.0 * payoff_swap)
        payoff_swaption = tf.math.maximum(payoff_swap, 0.0)
        option_value = tf.reshape(tf.math.reduce_mean(payoff_swaption, axis=0),
                                  output_shape)

        return notional * option_value
Esempio n. 3
0
def price(*,
          expiries,
          fixed_leg_payment_times,
          fixed_leg_daycount_fractions,
          fixed_leg_coupon,
          reference_rate_fn,
          num_hjm_factors,
          mean_reversion,
          volatility,
          times=None,
          time_step=None,
          num_time_steps=None,
          curve_times=None,
          corr_matrix=None,
          notional=None,
          is_payer_swaption=None,
          valuation_method=vm.ValuationMethod.MONTE_CARLO,
          num_samples=1,
          random_type=None,
          seed=None,
          skip=0,
          time_step_finite_difference=None,
          num_time_steps_finite_difference=None,
          num_grid_points_finite_difference=101,
          dtype=None,
          name=None):
    """Calculates the price of European swaptions using the HJM model.

  A European Swaption is a contract that gives the holder an option to enter a
  swap contract at a future date at a prespecified fixed rate. A swaption that
  grants the holder the right to pay fixed rate and receive floating rate is
  called a payer swaption while the swaption that grants the holder the right to
  receive fixed and pay floating payments is called the receiver swaption.
  Typically the start date (or the inception date) of the swap coincides with
  the expiry of the swaption. Mid-curve swaptions are currently not supported
  (b/160061740).

  This implementation uses the HJM model to numerically value European
  swaptions. For more information on the formulation of the HJM model, see
  quasi_gaussian_hjm.py.

  #### Example

  ````python
  import numpy as np
  import tensorflow.compat.v2 as tf
  import tf_quant_finance as tff

  dtype = tf.float64

  # Price 1y x 1y swaption with quarterly payments using Monte Carlo
  # simulations.
  expiries = np.array([1.0])
  fixed_leg_payment_times = np.array([1.25, 1.5, 1.75, 2.0])
  fixed_leg_daycount_fractions = 0.25 * np.ones_like(fixed_leg_payment_times)
  fixed_leg_coupon = 0.011 * np.ones_like(fixed_leg_payment_times)
  zero_rate_fn = lambda x: 0.01 * tf.ones_like(x, dtype=dtype)
  mean_reversion = [0.03]
  volatility = [0.02]

  price = tff.models.hjm.swaption_price(
      expiries=expiries,
      fixed_leg_payment_times=fixed_leg_payment_times,
      fixed_leg_daycount_fractions=fixed_leg_daycount_fractions,
      fixed_leg_coupon=fixed_leg_coupon,
      reference_rate_fn=zero_rate_fn,
      notional=100.,
      num_hjm_factors=1,
      mean_reversion=mean_reversion,
      volatility=volatility,
      valuation_method=tff.model.ValuationMethod.MONTE_CARLO,
      num_samples=500000,
      time_step=0.1,
      random_type=tff.math.random.RandomType.STATELESS_ANTITHETIC,
      seed=[1, 2])
  # Expected value: [[0.716]]
  ````


  #### References:
    [1]: D. Brigo, F. Mercurio. Interest Rate Models-Theory and Practice.
    Second Edition. 2007. Section 6.7, page 237.

  Args:
    expiries: A real `Tensor` of any shape and dtype. The time to expiration of
      the swaptions. The shape of this input along with the batch shape of the
      HJM model determines the number (and shape) of swaptions to be priced and
      the shape of the output. If the batch shape of HJM models is
      `model_batch_shape`, then the leading dimensions of `expiries` must be
      broadcastable to `model_batch_shape`. For example, if the rank of
      `model_batch_shape` is `n` and the rank of `expiries.shape` is `m`, then
      `m>=n` and the leading `n` dimensions of `expiries.shape` must be
      broadcastable to `model_batch_shape`.
    fixed_leg_payment_times: A real `Tensor` of the same dtype as `expiries`.
      The payment times for each payment in the fixed leg. The shape of this
      input should be `expiries.shape + [n]` where `n` denotes the number of
      fixed payments in each leg. The `fixed_leg_payment_times` should be
      greater-than or equal-to the corresponding expiries.
    fixed_leg_daycount_fractions: A real `Tensor` of the same dtype and
      compatible shape as `fixed_leg_payment_times`. The daycount fractions for
      each payment in the fixed leg.
    fixed_leg_coupon: A real `Tensor` of the same dtype and compatible shape as
      `fixed_leg_payment_times`. The fixed rate for each payment in the fixed
      leg.
    reference_rate_fn: A Python callable that accepts expiry time as a real
      `Tensor` and returns a `Tensor` of shape
      `model_batch_shape + input_shape`. Returns the continuously compounded
      zero rate at the present time for the input expiry time.
    num_hjm_factors: A Python scalar which corresponds to the number of factors
      in the batch of HJM models to be used for pricing.
    mean_reversion: A real positive `Tensor` of shape
      `model_batch_shape + [num_hjm_factors]`.
      Corresponds to the mean reversion rate of each factor in the batch.
    volatility: A real positive `Tensor` of the same `dtype` and shape as
        `mean_reversion` or a callable with the following properties:
        (a)  The callable should accept a scalar `Tensor` `t` and a `Tensor`
        `r(t)` of shape `batch_shape + [num_samples]` and returns a `Tensor` of
        shape compatible with `batch_shape + [num_samples, dim]`. The variable
        `t`  stands for time and `r(t)` is the short rate at time `t`. The
        function returns instantaneous volatility `sigma(t) = sigma(t, r(t))`.
        When `volatility` is specified as a real `Tensor`, each factor is
        assumed to have a constant instantaneous volatility  and the  model is
        effectively a Gaussian HJM model.
        Corresponds to the instantaneous volatility of each factor.
    times: An optional rank 1 `Tensor` of increasing positive real values. The
      times at which Monte Carlo simulations are performed. Relevant when
      swaption valuation is done using Monte Calro simulations.
      Default value: `None` in which case simulation times are computed based
      on either `time_step` or `num_time_steps` inputs.
    time_step: Optional scalar real `Tensor`. Maximal distance between time
      grid points in Euler scheme. Relevant when Euler scheme is used for
      simulation. This input or `num_time_steps` are required when valuation
      method is Monte Carlo.
      Default Value: `None`.
    num_time_steps: An optional scalar integer `Tensor` - a total number of
      time steps during Monte Carlo simulations. The maximal distance betwen
      points in grid is bounded by
      `times[-1] / (num_time_steps - times.shape[0])`.
      Either this or `time_step` should be supplied when the valuation method
      is Monte Carlo.
      Default value: `None`.
    curve_times: An optional rank 1 `Tensor` of positive and increasing real
      values. The maturities at which spot discount curve is computed during
      simulations.
      Default value: `None` in which case `curve_times` is computed based on
      swaption expiries and `fixed_leg_payments_times` inputs.
    corr_matrix: A `Tensor` of shape `[num_hjm_factors, num_hjm_factors]` and
      the same `dtype` as `mean_reversion`. Specifies the correlation between
      HJM factors.
      Default value: `None` in which case the factors are assumed to be
        uncorrelated.
    notional: An optional `Tensor` of same dtype and compatible shape as
      `strikes`specifying the notional amount for the underlying swaps.
       Default value: None in which case the notional is set to 1.
    is_payer_swaption: A boolean `Tensor` of a shape compatible with `expiries`.
      Indicates whether the swaption is a payer (if True) or a receiver (if
      False) swaption. If not supplied, payer swaptions are assumed.
    valuation_method: An enum of type `ValuationMethod` specifying
      the method to be used for swaption valuation. Currently the valuation is
      supported using `MONTE_CARLO` and `FINITE_DIFFERENCE` methods. Valuation
      using finite difference is only supported for Gaussian HJM models, i.e.
      for models with constant mean-reversion rate and time-dependent
      volatility.
      Default value: `ValuationMethod.MONTE_CARLO`, in which case
      swaption valuation is done using Monte Carlo simulations.
    num_samples: Positive scalar `int32` `Tensor`. The number of simulation
      paths during Monte-Carlo valuation. This input is ignored during analytic
      valuation.
      Default value: The default value is 1.
    random_type: Enum value of `RandomType`. The type of (quasi)-random number
      generator to use to generate the simulation paths. This input is relevant
      only for Monte-Carlo valuation and ignored during analytic valuation.
      Default value: `None` which maps to the standard pseudo-random numbers.
    seed: Seed for the random number generator. The seed is only relevant if
      `random_type` is one of `[STATELESS, PSEUDO, HALTON_RANDOMIZED,
      PSEUDO_ANTITHETIC, STATELESS_ANTITHETIC]`. For `PSEUDO`,
      `PSEUDO_ANTITHETIC` and `HALTON_RANDOMIZED` the seed should be an Python
      integer. For `STATELESS` and  `STATELESS_ANTITHETIC` must be supplied as
      an integer `Tensor` of shape `[2]`. This input is relevant only for
      Monte-Carlo valuation and ignored during analytic valuation.
      Default value: `None` which means no seed is set.
    skip: `int32` 0-d `Tensor`. The number of initial points of the Sobol or
      Halton sequence to skip. Used only when `random_type` is 'SOBOL',
      'HALTON', or 'HALTON_RANDOMIZED', otherwise ignored.
      Default value: `0`.
    time_step_finite_difference: Optional scalar real `Tensor`. Spacing between
      time grid points in finite difference discretization. This input is only
      relevant for valuation using finite difference.
      Default value: `None`. If `num_time_steps_finite_difference` is also
      unspecified then a `time_step` corresponding to 100 discretization steps
      is used.
    num_time_steps_finite_difference: Optional scalar real `Tensor`. Number of
      time grid points in finite difference discretization. This input is only
      relevant for valuation using finite difference.
      Default value: `None`. If `time_step_finite_difference` is also
      unspecified, then 100 time steps are used.
    num_grid_points_finite_difference: Optional scalar real `Tensor`. Number of
      spatial grid points per dimension. Currently, we construct an uniform grid
      for spatial discretization. This input is only relevant for valuation
      using finite difference.
      Default value: 101.
    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.
    name: Python string. The name to give to the ops created by this function.
      Default value: `None` which maps to the default name `hjm_swaption_price`.

  Returns:
    A `Tensor` of real dtype and shape derived from `model_batch_shape` and
    expiries.shape containing the computed swaption prices. The shape of the
    output is as follows:
      * If the `model_batch_shape` is [], then the shape of the output is
        expiries.shape + [1]
      * Otherwise, the shape of the output is
        `model_batch_shape + expiries.shape[model_batch_shape.rank:] + [1]`
    For swaptions that have reset in the past (expiries<0), the function sets
    the corresponding option prices to 0.0.
  """

    # TODO(b/160061740): Extend the functionality to support mid-curve swaptions.
    name = name or 'hjm_swaption_price'
    with tf.name_scope(name):
        expiries = tf.convert_to_tensor(expiries, dtype=dtype, name='expiries')
        dtype = dtype or expiries.dtype
        fixed_leg_payment_times = tf.convert_to_tensor(
            fixed_leg_payment_times,
            dtype=dtype,
            name='fixed_leg_payment_times')
        fixed_leg_daycount_fractions = tf.convert_to_tensor(
            fixed_leg_daycount_fractions,
            dtype=dtype,
            name='fixed_leg_daycount_fractions')
        fixed_leg_coupon = tf.convert_to_tensor(fixed_leg_coupon,
                                                dtype=dtype,
                                                name='fixed_leg_coupon')
        notional = tf.convert_to_tensor(notional, dtype=dtype, name='notional')
        notional = tf.expand_dims(tf.broadcast_to(notional, expiries.shape),
                                  axis=-1)
        if is_payer_swaption is None:
            is_payer_swaption = True
        is_payer_swaption = tf.convert_to_tensor(is_payer_swaption,
                                                 dtype=tf.bool,
                                                 name='is_payer_swaption')

        # Add a dimension corresponding to multiple cashflows in a swap
        if expiries.shape.rank == fixed_leg_payment_times.shape.rank - 1:
            expiries = tf.expand_dims(expiries, axis=-1)
        elif expiries.shape.rank < fixed_leg_payment_times.shape.rank - 1:
            raise ValueError(
                'Swaption expiries not specified for all swaptions '
                'in the batch. Expected rank {} but received {}.'.format(
                    fixed_leg_payment_times.shape.rank - 1,
                    expiries.shape.rank))

        # Expected shape: batch_shape + [m], where m is the number of fixed leg
        # payments per underlying swap. This is the same as
        # fixed_leg_payment_times.shape
        #
        # We need to explicitly use tf.repeat because we need to price
        # batch_shape + [m] bond options with different strikes along the last
        # dimension.
        expiries = tf.repeat(expiries,
                             tf.shape(fixed_leg_payment_times)[-1],
                             axis=-1)

        if valuation_method == vm.ValuationMethod.FINITE_DIFFERENCE:
            model = gaussian_hjm.GaussianHJM(
                num_hjm_factors,
                mean_reversion=mean_reversion,
                volatility=volatility,
                initial_discount_rate_fn=reference_rate_fn,
                corr_matrix=corr_matrix,
                dtype=dtype)

            # TODO(b/192294347): Enable pricing using batch of HJM models.
            if reference_rate_fn(tf.constant([0.0],
                                             dtype=dtype)).shape.rank > 1:
                raise ValueError(
                    'Pricing swaptions using a batch of HJM models with '
                    'finite differences is not currently supported.')
            instrument_batch_shape = expiries.shape.as_list()[:-1] or [1]
            return _bermudan_swaption_fd(
                instrument_batch_shape,
                model,
                # Add a dimension to denote ONE exercise date
                tf.expand_dims(expiries, axis=-2),
                fixed_leg_payment_times,
                fixed_leg_daycount_fractions,
                fixed_leg_coupon,
                notional,
                is_payer_swaption,
                time_step_finite_difference,
                num_time_steps_finite_difference,
                num_grid_points_finite_difference,
                name + '_fd',
                dtype)
        elif valuation_method == vm.ValuationMethod.MONTE_CARLO:
            # Monte-Carlo pricing
            model = quasi_gaussian_hjm.QuasiGaussianHJM(
                num_hjm_factors,
                mean_reversion=mean_reversion,
                volatility=volatility,
                initial_discount_rate_fn=reference_rate_fn,
                corr_matrix=corr_matrix,
                dtype=dtype)

            return _european_swaption_mc(
                model, expiries, fixed_leg_payment_times,
                fixed_leg_daycount_fractions, fixed_leg_coupon, notional,
                is_payer_swaption, times, time_step, num_time_steps,
                curve_times, num_samples, random_type, skip, seed, dtype,
                name + '_mc')
        else:
            raise ValueError(
                'Swaption Valuation using {} is not supported'.format(
                    str(valuation_method)))