Exemplo n.º 1
0
def implied_vol(prices,
                strikes,
                expiries,
                spots=None,
                forwards=None,
                discount_factors=None,
                is_call_options=None,
                method=ImpliedVolMethod.NEWTON,
                validate_args=False,
                dtype=None,
                name=None,
                **kwargs):
    """Finds the implied volatilities of options under the Black Scholes model.

  #### Examples
  ```python
  forwards = np.array([1.0, 1.0, 1.0, 1.0])
  strikes = np.array([1.0, 2.0, 1.0, 0.5])
  expiries = np.array([1.0, 1.0, 1.0, 1.0])
  discount_factors = np.array([1.0, 1.0, 1.0, 1.0])
  option_signs = np.array([1.0, 1.0, -1.0, -1.0])
  volatilities = np.array([1.0, 1.0, 1.0, 1.0])
  prices = black_scholes.option_price(
      forwards,
      strikes,
      volatilities,
      expiries,
      discount_factors=discount_factors,
      is_call_options=is_call_options)
  implied_vols = newton_vol.implied_vol(forwards,
                                        strikes,
                                        expiries,
                                        discount_factors,
                                        prices,
                                        option_signs)
  with tf.Session() as session:
    print(session.run(implied_vols[0]))
  # Expected output:
  # [ 1.  1.  1.  1.]

  Args:
    prices: A real `Tensor` of any shape. The prices of the options whose
      implied vol is to be calculated.
    strikes: A real `Tensor` of the same dtype as `prices` and a shape that
      broadcasts with `prices`. The strikes of the options.
    expiries: A real `Tensor` of the same dtype as `prices` and a shape that
      broadcasts with `prices`. The expiry for 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
      `prices`. The current spot price of the underlying. Either this argument
      or the `forwards` (but not both) must be supplied.
    forwards: A real `Tensor` of any shape that broadcasts to the shape of
      `prices`. The forwards to maturity. Either this argument or the `spots`
      must be supplied but both must not be supplied.
    discount_factors: An optional real `Tensor` of same dtype as the `prices`.
      If not None, these are the discount factors to expiry (i.e. e^(-rT)). If
      None, no discounting is applied (i.e. it is assumed that the undiscounted
      option prices are provided ). If `spots` is supplied and
      `discount_factors` is not None then this is also used to compute the
      forwards to expiry.
      Default value: None, equivalent to discount factors = 1.
    is_call_options: A boolean `Tensor` of a shape compatible with `prices`.
      Indicates whether the option is a call (if True) or a put (if False). If
      not supplied, call options are assumed.
    method: Enum value of ImpliedVolMethod to select the algorithm to use to
      infer the implied volatility.
    validate_args: A Python bool. If True, indicates that arguments should be
      checked for correctness before performing the computation. The checks
      performed are: (1) Forwards and strikes are positive. (2) The prices
        satisfy the arbitrage bounds (i.e. for call options, checks the
        inequality `max(F-K, 0) <= Price <= F` and for put options, checks that
        `max(K-F, 0) <= Price <= K`.). (3) Checks that the prices are not too
        close to the bounds. It is numerically unstable to compute the implied
        vols from options too far in the money or out of the money.
    dtype: `tf.Dtype` to use when converting arguments to `Tensor`s. If not
      supplied, the default TensorFlow conversion will take place. Note that
      this argument does not do any casting for `Tensor`s or numpy arrays.
      Default value: None.
    name: (Optional) Python str. The name prefixed to the ops created by this
      function. If not supplied, the default name 'implied_vol' is used.
      Default value: None
    **kwargs: Any other keyword arguments to be passed to the specific
      implementation. (See black_scholes.implied_vol_approx and
      black_scholes.implied_vol_newton for details).

  Returns:
    implied_vols: A `Tensor` of the same dtype as `prices` and shape as the
      common broadcasted shape of `(prices, spots/forwards, strikes, expiries)`.
      The implied volatilities as inferred by the chosen method.

  Raises:
    ValueError: If both `forwards` and `spots` are supplied or if neither is
      supplied.
  """
    if method == ImpliedVolMethod.FAST_APPROX:
        return approx.implied_vol(prices,
                                  strikes,
                                  expiries,
                                  spots=spots,
                                  forwards=forwards,
                                  discount_factors=discount_factors,
                                  is_call_options=is_call_options,
                                  validate_args=validate_args,
                                  dtype=dtype,
                                  name=name,
                                  **kwargs)
    if method == ImpliedVolMethod.NEWTON:
        return newton.implied_vol(prices,
                                  strikes,
                                  expiries,
                                  spots=spots,
                                  forwards=forwards,
                                  discount_factors=discount_factors,
                                  is_call_options=is_call_options,
                                  validate_args=validate_args,
                                  dtype=dtype,
                                  name=name,
                                  **kwargs)[0]
    raise ValueError('Unknown implied vol method {}'.format(method))
def implied_vol(
        *,
        prices,
        strikes,
        expiries,
        spots=None,
        forwards=None,
        discount_factors=None,
        is_call_options=None,
        initial_volatilities=None,
        underlying_distribution=utils.UnderlyingDistribution.LOG_NORMAL,
        tolerance=1e-8,
        max_iterations=20,
        validate_args=False,
        dtype=None,
        name=None):
    """Computes implied volatilities from given call or put option prices.

  This method applies a Newton root search algorithm to back out the implied
  volatility given the price of either a put or a call option.

  The implementation assumes that each cell in the supplied tensors corresponds
  to an independent volatility to find.

  Args:
    prices: A real `Tensor` of any shape. The prices of the options whose
      implied vol is to be calculated.
    strikes: A real `Tensor` of the same dtype as `prices` and a shape that
      broadcasts with `prices`. The strikes of the options.
    expiries: A real `Tensor` of the same dtype as `prices` and a shape that
      broadcasts with `prices`. The expiry for 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
      `prices`. The current spot price of the underlying. Either this argument
      or the `forwards` (but not both) must be supplied.
      Default value: None.
    forwards: A real `Tensor` of any shape that broadcasts to the shape of
      `prices`. The forwards to maturity. Either this argument or the `spots`
      must be supplied but both must not be supplied.
      Default value: None.
    discount_factors: An optional real `Tensor` of same dtype as the `prices`.
      If not None, these are the discount factors to expiry (i.e. e^(-rT)). If
      None, no discounting is applied (i.e. it is assumed that the undiscounted
      option prices are provided ). If `spots` is supplied and
      `discount_factors` is not None then this is also used to compute the
      forwards to expiry.
      Default value: None, equivalent to discount factors = 1.
    is_call_options: A boolean `Tensor` of a shape compatible with `prices`.
      Indicates whether the option is a call (if True) or a put (if False). If
      not supplied, call options are assumed.
      Default value: None.
    initial_volatilities: A real `Tensor` of the same shape and dtype as
      `forwards`. The starting positions for Newton's method.
      Default value: None. If not supplied, the starting point is chosen using
        the Stefanica-Radoicic scheme. See `polya_approx.implied_vol` for
        details.
      Default value: None.
    underlying_distribution: Enum value of ImpliedVolUnderlyingDistribution to
      select the distribution of the underlying.
      Default value: UnderlyingDistribution.LOG_NORMAL
    tolerance: `float`. The root finder will stop where this tolerance is
      crossed.
    max_iterations: `int`. The maximum number of iterations of Newton's method.
      Default value: 20.
    validate_args: A Python bool. If True, indicates that arguments should be
      checked for correctness before performing the computation. The checks
      performed are: (1) Forwards and strikes are positive. (2) The prices
        satisfy the arbitrage bounds (i.e. for call options, checks the
        inequality `max(F-K, 0) <= Price <= F` and for put options, checks that
        `max(K-F, 0) <= Price <= K`.). (3) Checks that the prices are not too
        close to the bounds. It is numerically unstable to compute the implied
        vols from options too far in the money or out of the money.
      Default value: False.
    dtype: `tf.Dtype` to use when converting arguments to `Tensor`s. If not
      supplied, the default TensorFlow conversion will take place. Note that
      this argument does not do any casting for `Tensor`s or numpy arrays.
      Default value: None.
    name: (Optional) Python str. The name prefixed to the ops created by this
      function. If not supplied, the default name 'implied_vol' is used.
      Default value: None.

  Returns:
    A 3-tuple containing the following items in order:
       (a) implied_vols: A `Tensor` of the same dtype as `prices` and shape as
         the common broadcasted shape of
         `(prices, spots/forwards, strikes, expiries)`. The implied vols as
         inferred by the algorithm. It is possible that the search may not have
         converged or may have produced NaNs. This can be checked for using the
         following return values.
       (b) converged: A boolean `Tensor` of the same shape as `implied_vols`
         above. Indicates whether the corresponding vol has converged to within
         tolerance.
       (c) failed: A boolean `Tensor` of the same shape as `implied_vols` above.
         Indicates whether the corresponding vol is NaN or not a finite number.
         Note that converged being True implies that failed will be false.
         However, it may happen that converged is False but failed is not True.
         This indicates the search did not converge in the permitted number of
         iterations but may converge if the iterations are increased.

  Raises:
    ValueError: If both `forwards` and `spots` are supplied or if neither is
      supplied.
  """
    if (spots is None) == (forwards is None):
        raise ValueError(
            'Either spots or forwards must be supplied but not both.')

    with tf.compat.v1.name_scope(name,
                                 default_name='implied_vol',
                                 values=[
                                     prices, spots, forwards, strikes,
                                     expiries, discount_factors,
                                     is_call_options, initial_volatilities
                                 ]):
        prices = tf.convert_to_tensor(prices, dtype=dtype, name='prices')
        dtype = prices.dtype
        strikes = tf.convert_to_tensor(strikes, dtype=dtype, name='strikes')
        expiries = tf.convert_to_tensor(expiries, dtype=dtype, name='expiries')
        if discount_factors is None:
            discount_factors = tf.convert_to_tensor(1.0,
                                                    dtype=dtype,
                                                    name='discount_factors')
        else:
            discount_factors = tf.convert_to_tensor(discount_factors,
                                                    dtype=dtype,
                                                    name='discount_factors')

        if forwards is not None:
            forwards = tf.convert_to_tensor(forwards,
                                            dtype=dtype,
                                            name='forwards')
        else:
            spots = tf.convert_to_tensor(spots, dtype=dtype, name='spots')
            forwards = spots / discount_factors

        if initial_volatilities is None:
            if underlying_distribution is utils.UnderlyingDistribution.LOG_NORMAL:
                initial_volatilities = approx.implied_vol(
                    prices=prices,
                    strikes=strikes,
                    expiries=expiries,
                    forwards=forwards,
                    discount_factors=discount_factors,
                    is_call_options=is_call_options,
                    validate_args=validate_args)
            elif underlying_distribution is utils.UnderlyingDistribution.NORMAL:
                initial_volatilities = prices / _NORM_PDF_AT_ZERO
        else:
            initial_volatilities = tf.convert_to_tensor(
                initial_volatilities, dtype=dtype, name='initial_volatilities')

        implied_vols, converged, failed = _newton_implied_vol(
            prices, strikes, expiries, forwards, discount_factors,
            is_call_options, initial_volatilities, underlying_distribution,
            tolerance, max_iterations)
        return implied_vols, converged, failed