def test_call_put_parity(self): current_price = 100.0 interest_rate = 0.05 vol = 0.2 strike = 120.0 maturity = 1.0 call_price = util.black_scholes_call_price(current_price, interest_rate, vol, strike, maturity) put_price = util.black_scholes_put_price(current_price, interest_rate, vol, strike, maturity) total_price = current_price - strike * tf.exp( -interest_rate * maturity) with self.test_session() as session: call_price_eval = session.run(call_price) put_price_eval = session.run(put_price) total_price_eval = session.run(total_price) self.assertGreater(call_price_eval, 0.0) self.assertGreater(put_price_eval, 0.0) self.assertAlmostEqual(call_price_eval - put_price_eval, total_price_eval, delta=1e-5)
def test_european_derivative_price_delta_mc_close_to_black_scholes(self): current_price = tf.constant(100.0, dtype=tf.float32) r = 0.05 vol = tf.constant([[0.2]], dtype=tf.float32) strike = 100.0 maturity = 0.1 dt = 0.01 bs_call_price = util.black_scholes_call_price(current_price, r, vol, strike, maturity) bs_delta = monte_carlo_manager.sensitivity_autodiff( bs_call_price, current_price) num_samples = int(1e3) initial_states = tf.ones([num_samples, 1]) * current_price def _dynamics_op(log_s, t, dt): return dynamics.gbm_log_euler_step_nd(log_s, r, vol, t, dt, key=1) def _payoff_fn(log_s): return tf.exp(-r * maturity) * payoffs.call_payoff( tf.exp(log_s), strike) (_, _, outcomes) = monte_carlo_manager.non_callable_price_mc( tf.log(initial_states), _dynamics_op, _payoff_fn, maturity, num_samples, dt) mc_deltas = monte_carlo_manager.sensitivity_autodiff( outcomes, initial_states) mean_mc_deltas = tf.reduce_mean(mc_deltas) mc_deltas_std = tf.sqrt( tf.reduce_mean(mc_deltas**2) - (mean_mc_deltas**2)) with self.test_session() as session: bs_delta_eval = session.run(bs_delta) mean_mc_deltas_eval, mc_deltas_std_eval = session.run( (mean_mc_deltas, mc_deltas_std)) self.assertLessEqual( mean_mc_deltas_eval, bs_delta_eval + 3.0 * mc_deltas_std_eval / np.sqrt(num_samples)) self.assertGreaterEqual( mean_mc_deltas_eval, bs_delta_eval - 3.0 * mc_deltas_std_eval / np.sqrt(num_samples))
def test_european_call_estimator_converges_close_to_black_scholes(self): current_price = 100.0 r = interest_rate = 0.05 vol = 0.2 strike = 120.0 maturity = 0.5 dt = 0.001 discount = tf.exp(-r * maturity) tol = 5e-2 conf_level = 0.95 batch_size = int(1e4) k = key_placeholder = tf.placeholder(shape=(), dtype=tf.int32) max_num_steps = 1e5 bs_call_price = util.black_scholes_call_price(current_price, interest_rate, vol, strike, maturity) initial_state = tf.constant(current_price) dynamics_op = lambda s, t, dt: dynamics.gbm_euler_step( s, r, vol, t, dt, k) payoff_fn = lambda s: discount * payoffs.call_payoff(s, strike) (mean_est, mean_sq_est, _) = monte_carlo_manager.non_callable_price_mc( initial_state, dynamics_op, payoff_fn, maturity, batch_size, dt) with self.test_session() as session: (mean_est_eval, _, converged) = monte_carlo_manager.mc_estimator( mean_est, mean_sq_est, batch_size, key_placeholder, {}, tol, conf_level, max_num_steps, session) bs_call_price_eval = session.run(bs_call_price) self.assertTrue(converged) # Here the discretization bias would make these asserts fail with larger dt. self.assertLessEqual(mean_est_eval, bs_call_price_eval * (1.0 + tol)) self.assertGreaterEqual(mean_est_eval, bs_call_price_eval * (1.0 - tol))
def test_european_call_log_euler_mc_close_to_black_scholes(self): current_price = 100.0 r = interest_rate = 0.05 vol = 0.2 strike = 120.0 maturity = 0.5 dt = 0.01 discount = tf.exp(-r * maturity) bs_call_price = util.black_scholes_call_price(current_price, interest_rate, vol, strike, maturity) num_samples = int(1e4) initial_state = tf.constant(current_price) dynamics_op = lambda s, t, dt: dynamics.gbm_log_euler_step( s, r, vol, t, dt) payoff_fn = lambda s: discount * payoffs.call_payoff(tf.exp(s), strike) (mean_outcome, mean_sq_outcome, _) = monte_carlo_manager.non_callable_price_mc( tf.log(initial_state), dynamics_op, payoff_fn, maturity, num_samples, dt) std_outcomes = util.stddev_est(mean_outcome, mean_sq_outcome) with self.test_session() as session: bs_call_price_eval = session.run(bs_call_price) mean_outcome_eval, std_outcomes_eval = session.run( (mean_outcome, std_outcomes)) self.assertLessEqual( mean_outcome_eval, bs_call_price_eval + 3.0 * std_outcomes_eval / np.sqrt(num_samples)) self.assertGreaterEqual( mean_outcome_eval, bs_call_price_eval - 3.0 * std_outcomes_eval / np.sqrt(num_samples))