def test_polya_implied_vol_validate(self):
        """Test the Radiocic-Polya approx doesn't raise where it shouldn't."""
        np.random.seed(6589)
        n = 100
        dtypes = [np.float32, np.float64]
        for dtype in dtypes:
            volatilities = np.exp(np.random.randn(n) / 2)
            forwards = np.exp(np.random.randn(n))
            strikes = forwards * (1 + (np.random.rand(n) - 0.5) * 0.2)
            expiries = np.exp(np.random.randn(n))
            prices = self.evaluate(
                black_scholes.option_price(forwards,
                                           strikes,
                                           volatilities,
                                           expiries,
                                           dtype=dtype))

            implied_vols = self.evaluate(
                polya_approx.implied_vol(prices,
                                         forwards,
                                         strikes,
                                         expiries,
                                         validate_args=True,
                                         dtype=dtype))
            self.assertArrayNear(volatilities, implied_vols, 0.6)
Esempio n. 2
0
    def test_price_vol_and_expiry_scaling(self):
        """Tests that the price is invariant under vol->k vol, T->T/k**2."""
        np.random.seed(1234)
        n = 20
        forwards = np.exp(np.random.randn(n))
        volatilities = np.exp(np.random.randn(n) / 2)
        strikes = np.exp(np.random.randn(n))
        expiries = np.exp(np.random.randn(n))
        scaling = 5.0
        base_prices = self.evaluate(
            black_scholes.option_price(forwards, strikes, volatilities,
                                       expiries))
        scaled_prices = self.evaluate(
            black_scholes.option_price(forwards, strikes,
                                       volatilities * scaling,
                                       expiries / scaling / scaling))

        self.assertArrayNear(base_prices, scaled_prices, 1e-10)
Esempio n. 3
0
 def test_price_long_expiry_calls(self):
     """Tests that very long expiry call option behaves like the asset."""
     forwards = np.array([1.0, 1.0, 1.0, 1.0])
     strikes = np.array([1.1, 0.9, 1.1, 0.9])
     volatilities = np.array([0.1, 0.2, 0.5, 0.9])
     expiries = 1e10
     expected_prices = forwards
     computed_prices = self.evaluate(
         black_scholes.option_price(forwards, strikes, volatilities,
                                    expiries))
     self.assertArrayNear(expected_prices, computed_prices, 1e-10)
Esempio n. 4
0
 def test_price_long_expiry_puts(self):
     """Tests that very long expiry put option is worth the strike."""
     forwards = np.array([1.0, 1.0, 1.0, 1.0])
     strikes = np.array([0.1, 10.0, 3.0, 0.0001])
     volatilities = np.array([0.1, 0.2, 0.5, 0.9])
     expiries = 1e10
     expected_prices = strikes
     computed_prices = self.evaluate(
         black_scholes.option_price(forwards,
                                    strikes,
                                    volatilities,
                                    expiries,
                                    is_call_options=False))
     self.assertArrayNear(expected_prices, computed_prices, 1e-10)
Esempio n. 5
0
 def test_option_prices(self):
     """Tests that the BS prices are correct."""
     forwards = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
     strikes = np.array([3.0, 3.0, 3.0, 3.0, 3.0])
     volatilities = np.array([0.0001, 102.0, 2.0, 0.1, 0.4])
     expiries = 1.0
     computed_prices = self.evaluate(
         black_scholes.option_price(forwards, strikes, volatilities,
                                    expiries))
     expected_prices = np.array([
         0.0, 2.0, 2.0480684764112578, 1.0002029716043364,
         2.0730313058959933
     ])
     self.assertArrayNear(expected_prices, computed_prices, 1e-10)
Esempio n. 6
0
 def test_price_zero_expiry(self):
     """Tests that zero expiry is correctly handled."""
     # If the expiry is zero, the option's value should be correct.
     forwards = np.array([1.0, 1.0, 1.0, 1.0])
     strikes = np.array([1.1, 0.9, 1.1, 0.9])
     volatilities = np.array([0.1, 0.2, 0.5, 0.9])
     expiries = 0.0
     is_call_options = np.array([True, True, False, False])
     expected_prices = np.array([0.0, 0.1, 0.1, 0.0])
     computed_prices = self.evaluate(
         black_scholes.option_price(forwards,
                                    strikes,
                                    volatilities,
                                    expiries,
                                    is_call_options=is_call_options))
     self.assertArrayNear(expected_prices, computed_prices, 1e-10)
Esempio n. 7
0
  def test_polya_implied_vol(self):
    """Basic test of the implied vol calculation."""
    np.random.seed(6589)
    n = 100
    dtypes = [np.float32, np.float64]
    for dtype in dtypes:
      volatilities = np.exp(np.random.randn(n).astype(dtype) / 2)
      forwards = np.exp(np.random.randn(n).astype(dtype))
      strikes = forwards * (1 + (np.random.rand(n).astype(dtype) - 0.5) * 0.2)
      expiries = np.exp(np.random.randn(n).astype(dtype))
      prices = self.evaluate(
          black_scholes.option_price(forwards, strikes, volatilities, expiries))

      implied_vols = self.evaluate(
          polya_approx.implied_vol(
              prices, forwards, strikes, expiries, dtype=dtype))
      self.assertArrayNear(volatilities, implied_vols, 0.6)
Esempio n. 8
0
    def test_implied_vol_extensive(self):

        np.random.seed(135)
        num_examples = 1000

        expiries = np.linspace(0.8, 1.2, num_examples)
        rates = np.linspace(0.03, 0.08, num_examples)
        discount_factors = np.exp(-rates * expiries)
        spots = np.ones(num_examples)
        forwards = spots / discount_factors
        strikes = np.linspace(0.8, 1.2, num_examples)
        volatilities = np.ones_like(forwards)
        call_options = np.random.binomial(n=1, p=0.5, size=num_examples)
        option_signs = 2.0 * call_options - 1.0
        is_call_options = np.array(call_options, dtype=np.bool)

        prices = self.evaluate(
            black_scholes.option_price(forwards,
                                       strikes,
                                       volatilities,
                                       expiries,
                                       is_call_options=is_call_options,
                                       discount_factors=discount_factors,
                                       dtype=tf.float64))

        implied_vols = self.evaluate(
            implied_vol(forwards,
                        strikes,
                        expiries,
                        discount_factors,
                        prices,
                        option_signs,
                        dtype=tf.float64,
                        max_iterations=1000,
                        tolerance=1e-8))[0]

        self.assertArrayNear(volatilities, implied_vols, 1e-7)
    def test_binary_vanilla_call_consistency(self):
        r"""Tests code consistency through relationship of binary and vanilla prices.

    With forward F, strike K, discount rate r, and expiry T, a vanilla call
    option should have price CV:

    $$ VC(K) = e^{-rT}( N(d_1)F - N(d_2)K ) $$

    A unit of cash paying binary call option should have price BC:

    $$ BC(K) = e^{-rT} N(d_2) $$

    Where d_1 and d_2 are standard Black-Scholes quanitities and depend on K
    through the ratio F/K. Hence for a small increment e:

    $$ (VC(K + e) - Vc(K))/e \approx -N(d_2)e^{-rT} = -BC(K + e) $$

    Similarly, for a vanilla put:

    $$ (VP(K + e) - VP(K))/e \approx N(-d_2)e^{-rT} = BP(K + e) $$

    This enables a test for consistency of pricing between vanilla and binary
    options prices.
    """

        np.random.seed(135)
        num_examples = 1000
        forwards = np.exp(np.random.normal(size=num_examples))
        strikes_0 = np.exp(np.random.normal(size=num_examples))
        epsilon = 1e-8
        strikes_1 = strikes_0 + epsilon
        volatilities = np.exp(np.random.normal(size=num_examples))
        expiries = np.random.gamma(shape=1.0, scale=1.0, size=num_examples)
        call_options = np.random.binomial(n=1, p=0.5, size=num_examples)
        is_call_options = np.array(call_options, dtype=np.bool)
        discount_factors = np.ones_like(forwards)

        option_prices_0 = self.evaluate(
            black_scholes.option_price(forwards,
                                       strikes_0,
                                       volatilities,
                                       expiries,
                                       is_call_options=is_call_options,
                                       dtype=tf.float64))

        option_prices_1 = self.evaluate(
            black_scholes.option_price(forwards,
                                       strikes_1,
                                       volatilities,
                                       expiries,
                                       is_call_options=is_call_options,
                                       dtype=tf.float64))

        binary_approximation = (-1.0)**call_options * (
            option_prices_1 - option_prices_0) / epsilon

        binary_prices = self.evaluate(
            black_scholes.binary_price(forwards,
                                       strikes_1,
                                       volatilities,
                                       expiries,
                                       is_call_options=is_call_options,
                                       discount_factors=discount_factors))

        self.assertArrayNear(binary_approximation, binary_prices, 1e-6)