def test_pandas(self): """Test with pandas input.""" count = int(1e3) maturity = 30 / 365 call = True sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) table = pd.DataFrame({'premium': premium, 'moneyness': moneyness}) table['maturity'] = maturity table['call'] = call table['imp_vol'] = impvol_table(table) self.assertIsInstance(table, pd.DataFrame) np.testing.assert_array_almost_equal(table['imp_vol'].values, sigma, 3) dct = { 'premium': premium, 'moneyness': moneyness, 'maturity': maturity, 'call': call } dct['imp_vol'] = impvol_table(dct) self.assertIsInstance(dct, dict) np.testing.assert_array_almost_equal(dct['imp_vol'], sigma, 3)
def test_gbm(self): """Test GBM model.""" price, strike = 100, 110 riskfree, maturity = .01, 30/365 call = True put = np.logical_not(call) moneyness = lfmoneyness(price, strike, riskfree, maturity) sigma = .15 model = GBM(GBMParam(sigma=sigma), riskfree, maturity) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) self.assertEqual(premium.shape, (1,)) np.testing.assert_array_almost_equal(premium, premium_true, 3) np.testing.assert_array_almost_equal(impvol_model, sigma, 2) moneyness = np.linspace(0, .1, 10) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) impvol_true = np.ones_like(impvol_model) * sigma self.assertEqual(premium.shape, moneyness.shape) np.testing.assert_array_almost_equal(premium, premium_true, 2) np.testing.assert_array_almost_equal(impvol_model, impvol_true, 2) riskfree = np.zeros_like(moneyness) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) self.assertEqual(premium.shape, moneyness.shape) np.testing.assert_array_almost_equal(premium, premium_true, 3) np.testing.assert_array_almost_equal(impvol_model, sigma, 2) moneyness = np.linspace(-.1, 0, 10) premium = cosmethod(model, moneyness=moneyness, call=put) premium_true = blackscholes_norm(moneyness, maturity, sigma, put) impvol_model = impvol_bisection(moneyness, maturity, premium, put) np.testing.assert_array_almost_equal(premium, premium_true, 2) np.testing.assert_array_almost_equal(impvol_model, impvol_true, 2)
def test_gbm(self): """Test GBM model.""" price, strike = 100, 110 riskfree, maturity = .01, 30 / 365 call = True put = np.logical_not(call) moneyness = lfmoneyness(price, strike, riskfree, maturity) sigma = .15 model = GBM(GBMParam(sigma=sigma), riskfree, maturity) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) self.assertEqual(premium.shape, (1, )) np.testing.assert_array_almost_equal(premium, premium_true, 3) np.testing.assert_array_almost_equal(impvol_model, sigma, 2) moneyness = np.linspace(0, .1, 10) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) impvol_true = np.ones_like(impvol_model) * sigma self.assertEqual(premium.shape, moneyness.shape) np.testing.assert_array_almost_equal(premium, premium_true, 2) np.testing.assert_array_almost_equal(impvol_model, impvol_true, 2) riskfree = np.zeros_like(moneyness) premium = cosmethod(model, moneyness=moneyness, call=call) premium_true = blackscholes_norm(moneyness, maturity, sigma, call) impvol_model = impvol_bisection(moneyness, maturity, premium, call) self.assertEqual(premium.shape, moneyness.shape) np.testing.assert_array_almost_equal(premium, premium_true, 3) np.testing.assert_array_almost_equal(impvol_model, sigma, 2) moneyness = np.linspace(-.1, 0, 10) premium = cosmethod(model, moneyness=moneyness, call=put) premium_true = blackscholes_norm(moneyness, maturity, sigma, put) impvol_model = impvol_bisection(moneyness, maturity, premium, put) np.testing.assert_array_almost_equal(premium, premium_true, 2) np.testing.assert_array_almost_equal(impvol_model, impvol_true, 2)
def impvol_bisection(moneyness, maturity, premium, call, tol=1e-5, fcount=1e3): """Function to find BS Implied Vol using Bisection Method. Parameters ---------- moneyness : float Log-forward moneyness maturity : float Fraction of the year premium : float Option premium normalized by current asset price call : bool Call/put flag. True for call, False for put Returns ------- float Implied volatilities. Shape of the array is according to broadcasting rules. """ sig, sig_u, sig_d = .2, 1, 1e-3 count = 0 err = blackscholes_norm(moneyness, maturity, sig, call) - premium # repeat until error is sufficiently small # or counter hits fcount while abs(err) > tol and count < fcount: if err < 0: sig_d = sig sig = (sig_u + sig)/2 else: sig_u = sig sig = (sig_d + sig)/2 err = blackscholes_norm(moneyness, maturity, sig, call) - premium count += 1 # return NA if counter hit 1000 if count == fcount: return -1 else: return sig
def impvol_bisection(moneyness, maturity, premium, call, tol=1e-5, fcount=1e3): """Function to find BS Implied Vol using Bisection Method. Parameters ---------- moneyness : float Log-forward moneyness maturity : float Fraction of the year premium : float Option premium normalized by current asset price call : bool Call/put flag. True for call, False for put Returns ------- float Implied volatilities. Shape of the array is according to broadcasting rules. """ sig, sig_u, sig_d = .2, 1, 1e-3 count = 0 err = blackscholes_norm(moneyness, maturity, sig, call) - premium # repeat until error is sufficiently small # or counter hits fcount while abs(err) > tol and count < fcount: if err < 0: sig_d = sig sig = (sig_u + sig) / 2 else: sig_u = sig sig = (sig_d + sig) / 2 err = blackscholes_norm(moneyness, maturity, sig, call) - premium count += 1 # return NA if counter hit 1000 if count == fcount: return -1 else: return sig
def test_bs_prices(self): """Test accuracy of back and forth BS conversion.""" count = int(1e3) maturity = 30/365 call = True sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) vol = impvol_bisection(moneyness, maturity, premium, call) np.testing.assert_array_almost_equal(vol, sigma, 3)
def test_bs_prices(self): """Test accuracy of back and forth BS conversion.""" count = int(1e3) maturity = 30 / 365 call = True sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) vol = impvol_bisection(moneyness, maturity, premium, call) np.testing.assert_array_almost_equal(vol, sigma, 3)
def premium(self): """Get price given implied volatility. Returns ------- float array Option premium normalized by current underlying price """ return blackscholes_norm(self.data['moneyness'], self.data['maturity'], self.impvol(), self.data['call'])
def premium(self): """Black-Scholes option price formula. Parameters ---------- moneyness : float array Log-forward moneyness call : bool array Call/put flag. True for call, False for put Returns ------- float array Option premium normalized by current underlying price """ return blackscholes_norm(self.data['moneyness'], self.data['maturity'], self.sigma, self.data['call'])
def test_pandas(self): """Test with pandas input.""" count = int(1e3) maturity = 30/365 call = True sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) table = pd.DataFrame({'premium': premium, 'moneyness': moneyness}) table['maturity'] = maturity table['call'] = call table['imp_vol'] = impvol_table(table) self.assertIsInstance(table, pd.DataFrame) np.testing.assert_array_almost_equal(table['imp_vol'].values, sigma, 3) dct = {'premium': premium, 'moneyness': moneyness, 'maturity': maturity, 'call': call} dct['imp_vol'] = impvol_table(dct) self.assertIsInstance(dct, dict) np.testing.assert_array_almost_equal(dct['imp_vol'], sigma, 3)
def premium(self): """Mixture of log-normals option price formula. Parameters ---------- moneyness : float array Log-forward moneyness call : bool array Call/put flag. True for call, False for put Returns ------- float array Option premium normalized by current underlying price """ premium = 0 for weight, mean, std in zip(self.weights, self.means, self.stds): shift = (self.data['riskfree'] - mean) * self.data['maturity'] moneyness = np.array(self.data['moneyness']) + shift premium += weight * blackscholes_norm( moneyness, self.data['maturity'], std, self.data['call']) return premium
def premium(self): """Mixture of log-normals option price formula. Parameters ---------- moneyness : float array Log-forward moneyness call : bool array Call/put flag. True for call, False for put Returns ------- float array Option premium normalized by current underlying price """ premium = 0 for weight, mean, std in zip(self.weights, self.means, self.stds): shift = (self.data['riskfree'] - mean) * self.data['maturity'] moneyness = np.array(self.data['moneyness']) + shift premium += weight * blackscholes_norm(moneyness, self.data['maturity'], std, self.data['call']) return premium
if __name__ == '__main__': price = 1 strike = 1 riskfree = .02 maturity = 30/365 premium = .057 call = True moneyness = lfmoneyness(price, strike, riskfree, maturity) vol = impvol_bisection(moneyness, maturity, premium, call) count = int(1e2) sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) text = 'Time elapsed: %.2f seconds' time_start = time.time() # Based on SciPy root method vol = imp_vol(moneyness, maturity, premium, call) print(np.allclose(sigma, vol)) print(text % (time.time() - time_start)) time_start = time.time() # Based on bisection method vol = impvol_bisection(moneyness, maturity, premium, call) print('Relative difference (percent) = ', ((sigma/vol-1).max()*100)) print(np.allclose(vol, sigma, rtol=1e-3)) print(text % (time.time() - time_start))
if __name__ == '__main__': price = 1 strike = 1 riskfree = .02 maturity = 30 / 365 premium = .057 call = True moneyness = lfmoneyness(price, strike, riskfree, maturity) vol = impvol_bisection(moneyness, maturity, premium, call) count = int(1e2) sigma = np.random.uniform(.05, .8, count) moneyness = np.random.uniform(-.1, .1, count) premium = blackscholes_norm(moneyness, maturity, sigma, call) text = 'Time elapsed: %.2f seconds' time_start = time.time() # Based on SciPy root method vol = imp_vol(moneyness, maturity, premium, call) print(np.allclose(sigma, vol)) print(text % (time.time() - time_start)) time_start = time.time() # Based on bisection method vol = impvol_bisection(moneyness, maturity, premium, call) print('Relative difference (percent) = ', ((sigma / vol - 1).max() * 100)) print(np.allclose(vol, sigma, rtol=1e-3)) print(text % (time.time() - time_start))