code: def barrier_price(
      *,
      volatilities: types.RealTensor,
      strikes: types.RealTensor,
      expiries: types.RealTensor,
      spots: types.RealTensor,
      barriers: types.RealTensor,
      rebates: types.RealTensor = None,
      discount_rates: types.RealTensor = None,
      dividend_rates: types.RealTensor = None,
      is_barrier_down: types.BoolTensor = None,
      is_knock_out: types.BoolTensor = None,
      is_call_options: types.BoolTensor = None,
      dtype: tf.DType = None,
      name: str = None,
  ) -> types.RealTensor:
      """Prices barrier options in a Black-Scholes Model.
  
      Computes the prices of options with a single barrier in Black-Scholes world as
      described in Ref. [1]. Note that the barrier is applied continuously.
  
      #### Example
  
      This example is taken from Ref. [2], Page 154.
  
      ```python
      import tf_quant_finance as tff
  
      dtype = np.float32
      discount_rates = np.array([.08, .08])
      dividend_rates = np.array([.04, .04])
      spots = np.array([100., 100.])
      strikes = np.array([90., 90.])
      barriers = np.array([95. 95.])
      rebates = np.array([3. 3.])
      volatilities = np.array([.25, .25])
      expiries = np.array([.5, .5])
      barriers_type = np.array([5, 1])
      is_barrier_down = np.array([True, False])
      is_knock_out = np.array([False, False])
      is_call_option = np.array([True, True])
  
      price = tff.black_scholes.barrier_price(
        discount_rates, dividend_rates, spots, strikes,
        barriers, rebates, volatilities,
        expiries, is_barrier_down, is_knock_out, is_call_options)
  
      # Expected output
      #  `Tensor` with values [9.024, 7.7627]
      ```
  
      #### References
  
      [1]: Lee Clewlow, Javier Llanos, Chris Strickland, Caracas Venezuela
        Pricing Exotic Options in a Black-Scholes World, 1994
        https://warwick.ac.uk/fac/soc/wbs/subjects/finance/research/wpaperseries/1994/94-54.pdf
      [2]: Espen Gaarder Haug, The Complete Guide to Option Pricing Formulas,
        2nd Edition, 1997
  
      Args:
        volatilities: Real `Tensor` of any shape and dtype. The volatilities to
          expiry of the options to price.
        strikes: A real `Tensor` of the same dtype and compatible shape as
          `volatilities`. The strikes of the options to be priced.
        expiries: A real `Tensor` of same dtype and compatible shape as
          `volatilities`. The expiry of each option. The units should be such that
          `expiry * volatility**2` is dimensionless.
        spots: A real `Tensor` of any shape that broadcasts to the shape of the
          `volatilities`. The current spot price of the underlying.
        barriers: A real `Tensor` of same dtype as the `volatilities` and of the
          shape that broadcasts with `volatilities`. The barriers of each option.
        rebates: A real `Tensor` of same dtype as the `volatilities` and of the
          shape that broadcasts with `volatilities`. For knockouts, this is a
          fixed cash payout in case the barrier is breached. For knockins, this is a
          fixed cash payout in case the barrier level is not breached. In the former
          case, the rebate is paid immediately on breach whereas in the latter, the
          rebate is paid at the expiry of the option.
          Default value: `None` which maps to no rebates.
        discount_rates: A real `Tensor` of same dtype as the
          `volatilities` and of the shape that broadcasts with `volatilities`.
          Discount rates, or risk free rates.
          Default value: `None`, equivalent to discount_rate = 0.
        dividend_rates: A real `Tensor` of same dtype as the
          `volatilities` and of the shape that broadcasts with `volatilities`. A
          continuous dividend rate paid by the underlier. If `None`, then
          defaults to zero dividends.
          Default value: `None`, equivalent to zero dividends.
        is_barrier_down: A real `Tensor` of `boolean` values and of the shape
          that broadcasts with `volatilities`. True if barrier is below asset
          price at expiration.
          Default value: `True`.
        is_knock_out: A real `Tensor` of `boolean` values and of the shape
          that broadcasts with `volatilities`. True if option is knock out
          else false.
          Default value: `True`.
        is_call_options: A real `Tensor` of `boolean` values and of the shape
          that broadcasts with `volatilities`. True if option is call else
          false.
          Default value: `True`.
        dtype: Optional `tf.DType`. If supplied, the dtype to be used for conversion
          of any supplied non-`Tensor` arguments to `Tensor`.
          Default value: `None` which maps to the default dtype inferred by
          TensorFlow.
        name: str. The name for the ops created by this function.
          Default value: `None` which is mapped to the default name `barrier_price`.
      Returns:
        option_prices: A `Tensor` of same shape as `spots`. The approximate price of
        the barriers option under black scholes.
      """
      # The computation is done as in Ref [2] where each integral is split into
      # two matrices. The first matrix contains the algebraic terms and the second
      # matrix contains the probability distribution terms. Masks are used to filter
      # appropriate terms for calculating the integral. Then a dot product of each
      # row in the matricies coupled with the masks work to calculate the prices of
      # the barriers option.
      with tf.name_scope(name or "barrier_price"):
          spots = tf.convert_to_tensor(spots, dtype=dtype, name="spots")
          dtype = spots.dtype
          strikes = tf.convert_to_tensor(
              strikes, dtype=dtype, name="strikes"
          )
          volatilities = tf.convert_to_tensor(
              volatilities, dtype=dtype, name="volatilities"
          )
          expiries = tf.convert_to_tensor(
              expiries, dtype=dtype, name="expiries"
          )
          barriers = tf.convert_to_tensor(
              barriers, dtype=dtype, name="barriers"
          )
          if rebates is not None:
              rebates = tf.convert_to_tensor(
                  rebates, dtype=dtype, name="rebates"
              )
          else:
              rebates = tf.zeros_like(
                  spots, dtype=dtype, name="rebates"
              )
  
          # Convert all to tensor and enforce float dtype where required
          if discount_rates is not None:
              discount_rates = tf.convert_to_tensor(
                  discount_rates, dtype=dtype, name="discount_rates"
              )
          else:
              discount_rates = tf.convert_to_tensor(
                  0.0, dtype=dtype, name="discount_rates"
              )
  
          if dividend_rates is not None:
              dividend_rates = tf.convert_to_tensor(
                  dividend_rates, dtype=dtype, name="dividend_rates"
              )
          else:
              dividend_rates = tf.convert_to_tensor(
                  0.0, dtype=dtype, name="dividend_rates"
              )
  
          if is_barrier_down is None:
              is_barrier_down = tf.constant(1, name="is_barrier_down")
          else:
              is_barrier_down = tf.convert_to_tensor(
                  is_barrier_down, dtype=tf.bool, name="is_barrier_down"
              )
              is_barrier_down = tf.where(is_barrier_down, 1, 0)
          if is_knock_out is None:
              is_knock_out = tf.constant(1, name="is_knock_out")
          else:
              is_knock_out = tf.convert_to_tensor(
                  is_knock_out, dtype=tf.bool, name="is_knock_out"
              )
              is_knock_out = tf.where(is_knock_out, 1, 0)
          if is_call_options is None:
              is_call_options = tf.constant(1, name="is_call_options")
          else:
              is_call_options = tf.convert_to_tensor(
                  is_call_options, dtype=tf.bool, name="is_call_options"
              )
              is_call_options = tf.where(is_call_options, 1, 0)
  
          # Indices which range from 0-7 are used to select the appropriate
          # mask for each barrier
          indices = (
              tf.bitwise.left_shift(is_barrier_down, 2)
              + tf.bitwise.left_shift(is_knock_out, 1)
              + is_call_options
          )
  
          # Masks select the appropriate terms for integral approximations
          # Integrals are separated by algebraic terms and probability
          # distribution terms. This give 12 different terms per matrix
          # (6 integrals, 2 terms each)
          # shape = [8, 12]
          mask_matrix_greater_strike = tf.constant(
              [
                  [
                      1,
                      1,
                      -1,
                      -1,
                      0,
                      0,
                      1,
                      1,
                      1,
                      1,
                      0,
                      0,
                  ],  # up and in put
                  [
                      1,
                      1,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                      0,
                      0,
                  ],  # up and in call
                  [
                      0,
                      0,
                      1,
                      1,
                      0,
                      0,
                      -1,
                      -1,
                      0,
                      0,
                      1,
                      1,
                  ],  # up and out put
                  [
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                  ],  # up and out call
                  [
                      0,
                      0,
                      1,
                      1,
                      -1,
                      -1,
                      1,
                      1,
                      0,
                      0,
                      1,
                      1,
                  ],  # down and in put
                  [
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                      0,
                      0,
                      1,
                      1,
                      0,
                      0,
                  ],  # down and in call
                  [
                      1,
                      1,
                      -1,
                      -1,
                      1,
                      1,
                      -1,
                      -1,
                      0,
                      0,
                      1,
                      1,
                  ],  # down and out put
                  [1, 1, 0, 0, -1, -1, 0, 0, 0, 0, 1, 1],
              ]
          )  # down and out call
  
          mask_matrix_lower_strike = tf.constant(
              [
                  [0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0],  # up and in put
                  [
                      0,
                      0,
                      1,
                      1,
                      -1,
                      -1,
                      1,
                      1,
                      1,
                      1,
                      0,
                      0,
                  ],  # up and in call
                  [
                      1,
                      1,
                      0,
                      0,
                      -1,
                      -1,
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                  ],  # up and out put
                  [
                      1,
                      1,
                      -1,
                      -1,
                      1,
                      1,
                      -1,
                      -1,
                      0,
                      0,
                      1,
                      1,
                  ],  # up and out call
                  [
                      1,
                      1,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                      0,
                      0,
                  ],  # down and in put
                  [
                      1,
                      1,
                      -1,
                      -1,
                      0,
                      0,
                      1,
                      1,
                      1,
                      1,
                      0,
                      0,
                  ],  # down and in call
                  [
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      0,
                      1,
                      1,
                  ],  # down and out put
                  [0, 0, 1, 1, 0, 0, -1, -1, 0, 0, 1, 1],
              ]
          )  # down and out call
  
          # Create masks
          # Masks are shape [strikes.shape, 12]
          masks_lower = tf.gather(
              mask_matrix_lower_strike, indices, axis=0
          )
          masks_greater = tf.gather(
              mask_matrix_greater_strike, indices, axis=0
          )
          strikes_greater = tf.expand_dims(strikes > barriers, axis=-1)
          masks = tf.where(strikes_greater, masks_greater, masks_lower)
          masks = tf.cast(masks, dtype=dtype)
          one = tf.constant(1, dtype=dtype)
          call_or_put = tf.cast(
              tf.where(tf.equal(is_call_options, 0), -one, one),
              dtype=dtype,
          )
          below_or_above = tf.cast(
              tf.where(tf.equal(is_barrier_down, 0), -one, one),
              dtype=dtype,
          )
  
          # Calculate params for integrals
          sqrt_var = volatilities * tf.math.sqrt(expiries)
          mu = (discount_rates - dividend_rates) - (
              (volatilities**2) / 2
          )
          lamda = 1 + (mu / (volatilities**2))
          x = (tf.math.log(spots / strikes) / (sqrt_var)) + (
              lamda * sqrt_var
          )
          x1 = (tf.math.log(spots / barriers) / (sqrt_var)) + (
              lamda * sqrt_var
          )
          y = (
              tf.math.log((barriers**2) / (spots * strikes))
              / (sqrt_var)
          ) + (lamda * sqrt_var)
          y1 = (tf.math.log(barriers / spots) / (sqrt_var)) + (
              lamda * sqrt_var
          )
          b = (
              (mu**2) + (2 * (volatilities**2) * discount_rates)
          ) / (volatilities**2)
          z = (tf.math.log(barriers / spots) / (sqrt_var)) + (
              b * sqrt_var
          )
          a = mu / (volatilities**2)
  
          # Other params used for integrals
          discount_factors = tf.math.exp(
              -discount_rates * expiries, name="discount_factors"
          )
          barriers_ratio = tf.math.divide(
              barriers, spots, name="barriers_ratio"
          )
          spots_term = (
              call_or_put
              * spots
              * tf.math.exp(-dividend_rates * expiries)
          )
          strikes_term = call_or_put * strikes * discount_factors
  
          # rank is used to stack elements and reduce_sum
          strike_rank = strikes.shape.rank
  
          # Constructing Matrix with first and second algebraic terms for each
          # integral [strike.shape, 12]
          terms_mat = tf.stack(
              (
                  spots_term,
                  -strikes_term,
                  spots_term,
                  -strikes_term,
                  spots_term * (barriers_ratio ** (2 * lamda)),
                  -strikes_term * (barriers_ratio ** ((2 * lamda) - 2)),
                  spots_term * (barriers_ratio ** (2 * lamda)),
                  -strikes_term * (barriers_ratio ** ((2 * lamda) - 2)),
                  rebates * discount_factors,
                  -rebates
                  * discount_factors
                  * (  # pylint: disable=invalid-unary-operand-type
                      barriers_ratio ** ((2 * lamda) - 2)
                  ),
                  rebates * (barriers_ratio ** (a + b)),
                  rebates * (barriers_ratio ** (a - b)),
              ),
              name="term_matrix",
              axis=strike_rank,
          )
  
          # Constructing Matrix with first and second norm for each integral
          # [strikes.shape, 12]
          cdf_mat = tf.stack(
              (
                  call_or_put * x,
                  call_or_put * (x - sqrt_var),
                  call_or_put * x1,
                  call_or_put * (x1 - sqrt_var),
                  below_or_above * y,
                  below_or_above * (y - sqrt_var),
                  below_or_above * y1,
                  below_or_above * (y1 - sqrt_var),
                  below_or_above * (x1 - sqrt_var),
                  below_or_above * (y1 - sqrt_var),
                  below_or_above * z,
                  below_or_above * (z - (2 * b * sqrt_var)),
              ),
              name="cdf_matrix",
              axis=strike_rank,
          )
          cdf_mat = _ncdf(cdf_mat)
          # Calculating and returning price for each option
          return tf.reduce_sum(
              masks * terms_mat * cdf_mat, axis=strike_rank
          )
  
file_path: tf-quant-finance/tf_quant_finance/black_scholes/vanilla_prices.py
imports: import numpy
  import tensorflow.compat.v2
  from tf_quant_finance import types
label_desc: Use image node_id 2 for calling a global function with example usage: barrier_price() and returns: tf