def _blsimpv(price, spot, strike, risk_free_rate, time, option_type, dividend): spot = SimpleQuote(spot) daycounter = ActualActual(ISMA) risk_free_ts = FlatForward(today(), risk_free_rate, daycounter) dividend_ts = FlatForward(today(), dividend, daycounter) volatility_ts = BlackConstantVol(today(), NullCalendar(), .3, daycounter) process = BlackScholesMertonProcess(spot, dividend_ts, risk_free_ts, volatility_ts) exercise_date = today() + Period(time * 365, Days) exercise = EuropeanExercise(exercise_date) payoff = PlainVanillaPayoff(option_type, strike) option = EuropeanOption(payoff, exercise) engine = AnalyticEuropeanEngine(process) option.set_pricing_engine(engine) accuracy = 0.001 max_evaluations = 1000 min_vol = 0.01 max_vol = 2 vol = option.implied_volatility(price, process, accuracy, max_evaluations, min_vol, max_vol) return vol
def _blsprice(spot, strike, risk_free_rate, time, volatility, option_type='Call', dividend=0.0, calc='price'): """ Black-Scholes option pricing model + greeks. """ _spot = SimpleQuote(spot) daycounter = ActualActual(ISMA) risk_free_ts = FlatForward(today(), risk_free_rate, daycounter) dividend_ts = FlatForward(today(), dividend, daycounter) volatility_ts = BlackConstantVol(today(), NullCalendar(), volatility, daycounter) process = BlackScholesMertonProcess(_spot, dividend_ts, risk_free_ts, volatility_ts) exercise_date = today() + Period(time * 365, Days) exercise = EuropeanExercise(exercise_date) payoff = PlainVanillaPayoff(option_type, strike) option = EuropeanOption(payoff, exercise) engine = AnalyticEuropeanEngine(process) option.set_pricing_engine(engine) if calc == 'price': res = option.npv elif calc == 'delta': res = option.delta elif calc == 'gamma': res = option.gamma elif calc == 'theta': res = option.theta elif calc == 'rho': res = option.rho elif calc == 'vega': res = option.vega elif calc == 'lambda': res = option.delta * spot / option.npv else: raise ValueError('calc type %s is unknown' % calc) return res
def test_mc_variance_swap(self): """ test mc variance engine vs expected result """ vols = [] dates = [] interm_date = self.today + int(0.1 * 365 + 0.5) exercise = EuropeanExercise(self.ex_date) dates.append(interm_date) dates.append(self.ex_date) vols.append(0.1) vols.append(self.values['v']) # Exercising code using BlackVarianceCurve because BlackVarianceSurface # is unreliable. Result should be v*v for arbitrary t1 and v1 # (as long as 0<=t1<t and 0<=v1<v) vol_ts = BlackVarianceCurve(self.today, dates, vols, self.dc, True) stoch_process = BlackScholesMertonProcess(self.spot, self.q_ts, self.r_ts, vol_ts) engine = MCVarianceSwapEngine( stoch_process, time_steps_per_year=250, required_samples=1023, seed=42, ) variance_swap = VarianceSwap( self.values['type'], self.values['strike'], self.values['nominal'], self.today, self.ex_date, ) variance_swap.set_pricing_engine(engine) calculated = variance_swap.variance expected = 0.04 tol = 3.0e-4 error = abs(calculated - expected) self.assertTrue(error < tol)
def setUp(self): self.settings = Settings() self.calendar = NullCalendar() self.todays_date = Date(15, May, 1998) self.settlement_date = Date(17, May, 1998) self.settings.evaluation_date = self.todays_date # options parameters self.dividend_yield = 0.00 self.risk_free_rate = 0.06 self.volatility = 0.25 self.spot = SimpleQuote(100) self.maturity = Date(17, May, 1999) self.daycounter = Actual365Fixed() self.tol = 1e-2 # bootstrap the yield/dividend/vol curves dates = [self.settlement_date] + \ [self.settlement_date + Period(i + 1, Years) for i in range(40)] rates = [0.01] + \ [0.01 + 0.0002 * np.exp(np.sin(i / 4.0)) for i in range(40)] divRates = [0.02] + \ [0.02 + 0.0001 * np.exp(np.sin(i / 5.0)) for i in range(40)] self.r_ts = ZeroCurve(dates, rates, self.daycounter) self.q_ts = ZeroCurve(dates, divRates, self.daycounter) self.vol_ts = BlackConstantVol( self.settlement_date, self.calendar, self.volatility, self.daycounter ) self.black_scholes_merton_process = BlackScholesMertonProcess( self.spot, self.q_ts, self.r_ts, self.vol_ts ) self.dates = dates
def blsprice(spot, strike, risk_free_rate, time, volatility, option_type='Call', dividend=0.0): """ """ spot = SimpleQuote(spot) daycounter = Actual360() risk_free_ts = FlatForward(today(), risk_free_rate, daycounter) dividend_ts = FlatForward(today(), dividend, daycounter) volatility_ts = BlackConstantVol(today(), NullCalendar(), volatility, daycounter) process = BlackScholesMertonProcess(spot, dividend_ts, risk_free_ts, volatility_ts) exercise_date = today() + 90 exercise = EuropeanExercise(exercise_date) payoff = PlainVanillaPayoff(option_type, strike) option = EuropeanOption(payoff, exercise) engine = AnalyticEuropeanEngine(process) option.set_pricing_engine(engine) return option.npv
def test_bsm_hw(self): print("Testing European option pricing for a BSM process" + " with one-factor Hull-White model...") dc = Actual365Fixed() todays_date = today() maturity_date = todays_date + Period(20, Years) settings = Settings() settings.evaluation_date = todays_date spot = SimpleQuote(100) q_ts = flat_rate(todays_date, 0.04, dc) r_ts = flat_rate(todays_date, 0.0525, dc) vol_ts = BlackConstantVol(todays_date, NullCalendar(), 0.25, dc) hullWhiteModel = HullWhite(r_ts, 0.00883, 0.00526) bsm_process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts) exercise = EuropeanExercise(maturity_date) fwd = spot.value * q_ts.discount(maturity_date) / \ r_ts.discount(maturity_date) payoff = PlainVanillaPayoff(Call, fwd) option = VanillaOption(payoff, exercise) tol = 1e-8 corr = [-0.75, -0.25, 0.0, 0.25, 0.75] expectedVol = [ 0.217064577, 0.243995801, 0.256402830, 0.268236596, 0.290461343 ] for c, v in zip(corr, expectedVol): bsm_hw_engine = AnalyticBSMHullWhiteEngine(c, bsm_process, hullWhiteModel) option = VanillaOption(payoff, exercise) option.set_pricing_engine(bsm_hw_engine) npv = option.npv compVolTS = BlackConstantVol(todays_date, NullCalendar(), v, dc) bs_process = BlackScholesMertonProcess(spot, q_ts, r_ts, compVolTS) bsEngine = AnalyticEuropeanEngine(bs_process) comp = VanillaOption(payoff, exercise) comp.set_pricing_engine(bsEngine) impliedVol = comp.implied_volatility(npv, bs_process, 1e-10, 500, min_vol=0.1, max_vol=0.4) if (abs(impliedVol - v) > tol): print("Failed to reproduce implied volatility cor: %f" % c) print("calculated: %f" % impliedVol) print("expected : %f" % v) if abs((comp.npv - npv) / npv) > tol: print("Failed to reproduce NPV") print("calculated: %f" % comp.npv) print("expected : %f" % npv) self.assertAlmostEqual(impliedVol, v, delta=tol) self.assertAlmostEqual(comp.npv / npv, 1, delta=tol)
def test_compare_BsmHW_HestonHW(self): """ From Quantlib test suite """ print("Comparing European option pricing for a BSM " + "process with one-factor Hull-White model...") dc = Actual365Fixed() todays_date = today() settings = Settings() settings.evaluation_date = todays_date tol = 1.e-2 spot = SimpleQuote(100) dates = [todays_date + Period(i, Years) for i in range(40)] rates = [0.01 + 0.0002 * np.exp(np.sin(i / 4.0)) for i in range(40)] divRates = [0.02 + 0.0001 * np.exp(np.sin(i / 5.0)) for i in range(40)] s0 = SimpleQuote(100) r_ts = ZeroCurve(dates, rates, dc) q_ts = ZeroCurve(dates, divRates, dc) vol = SimpleQuote(0.25) vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol.value, dc) bsm_process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts) variance = vol.value * vol.value hestonProcess = HestonProcess(risk_free_rate_ts=r_ts, dividend_ts=q_ts, s0=s0, v0=variance, kappa=5.0, theta=variance, sigma=1e-4, rho=0.0) hestonModel = HestonModel(hestonProcess) hullWhiteModel = HullWhite(r_ts, a=0.01, sigma=0.01) bsmhwEngine = AnalyticBSMHullWhiteEngine(0.0, bsm_process, hullWhiteModel) hestonHwEngine = AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128) tol = 1e-5 strikes = [0.25, 0.5, 0.75, 0.8, 0.9, 1.0, 1.1, 1.2, 1.5, 2.0, 4.0] maturities = [1, 2, 3, 5, 10, 15, 20, 25, 30] types = [Put, Call] for type in types: for strike in strikes: for maturity in maturities: maturity_date = todays_date + Period(maturity, Years) exercise = EuropeanExercise(maturity_date) fwd = strike * s0.value * \ q_ts.discount(maturity_date) / \ r_ts.discount(maturity_date) payoff = PlainVanillaPayoff(type, fwd) option = VanillaOption(payoff, exercise) option.set_pricing_engine(bsmhwEngine) calculated = option.npv option.set_pricing_engine(hestonHwEngine) expected = option.npv if ((np.abs(expected - calculated) > calculated * tol) and (np.abs(expected - calculated) > tol)): cp = PAYOFF_TO_STR[type] print("Failed to reproduce npv") print("strike : %f" % strike) print("maturity : %d" % maturity) print("type : %s" % cp) self.assertAlmostEqual(expected, calculated, delta=tol)
def test_compare_bsm_bsmhw_hestonhw(self): dc = Actual365Fixed() todays_date = today() settings = Settings() settings.evaluation_date = todays_date tol = 1.e-2 spot = SimpleQuote(100) dates = [todays_date + Period(i, Years) for i in range(40)] rates = [0.01 + 0.0002 * np.exp(np.sin(i / 4.0)) for i in range(40)] divRates = [0.02 + 0.0001 * np.exp(np.sin(i / 5.0)) for i in range(40)] s0 = SimpleQuote(100) r_ts = ZeroCurve(dates, rates, dc) q_ts = ZeroCurve(dates, divRates, dc) vol = SimpleQuote(0.25) vol_ts = BlackConstantVol(todays_date, NullCalendar(), vol.value, dc) bsm_process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts) payoff = PlainVanillaPayoff(Call, 100) exercise = EuropeanExercise(dates[1]) option = VanillaOption(payoff, exercise) analytic_european_engine = AnalyticEuropeanEngine(bsm_process) option.set_pricing_engine(analytic_european_engine) npv_bsm = option.npv variance = vol.value * vol.value hestonProcess = HestonProcess(risk_free_rate_ts=r_ts, dividend_ts=q_ts, s0=s0, v0=variance, kappa=5.0, theta=variance, sigma=1e-4, rho=0.0) hestonModel = HestonModel(hestonProcess) hullWhiteModel = HullWhite(r_ts, a=0.01, sigma=0.01) bsmhwEngine = AnalyticBSMHullWhiteEngine(0.0, bsm_process, hullWhiteModel) hestonHwEngine = AnalyticHestonHullWhiteEngine(hestonModel, hullWhiteModel, 128) hestonEngine = AnalyticHestonEngine(hestonModel, 144) option.set_pricing_engine(hestonEngine) npv_heston = option.npv option.set_pricing_engine(bsmhwEngine) npv_bsmhw = option.npv option.set_pricing_engine(hestonHwEngine) npv_hestonhw = option.npv print("calculated with BSM: %f" % npv_bsm) print("BSM-HW: %f" % npv_bsmhw) print("Heston: %f" % npv_heston) print("Heston-HW: %f" % npv_hestonhw) self.assertAlmostEqual(npv_bsm, npv_bsmhw, delta=tol) self.assertAlmostEqual(npv_bsm, npv_hestonhw, delta=tol)
def test_replicating_variance_swap(self): """ data from "A Guide to Volatility and Variance Swaps", Derman, Kamal & Zou, 1999 with maturity t corrected from 0.25 to 0.246575 corresponding to Jan 1, 1999 to Apr 1, 1999 """ replicating_option_data = [ { 'type': OptionType.Put, 'strike': 50, 'v': 0.30 }, { 'type': OptionType.Put, 'strike': 55, 'v': 0.29 }, { 'type': OptionType.Put, 'strike': 60, 'v': 0.28 }, { 'type': OptionType.Put, 'strike': 65, 'v': 0.27 }, { 'type': OptionType.Put, 'strike': 70, 'v': 0.26 }, { 'type': OptionType.Put, 'strike': 75, 'v': 0.25 }, { 'type': OptionType.Put, 'strike': 80, 'v': 0.24 }, { 'type': OptionType.Put, 'strike': 85, 'v': 0.23 }, { 'type': OptionType.Put, 'strike': 90, 'v': 0.22 }, { 'type': OptionType.Put, 'strike': 95, 'v': 0.21 }, { 'type': OptionType.Put, 'strike': 100, 'v': 0.20 }, { 'type': OptionType.Call, 'strike': 100, 'v': 0.20 }, { 'type': OptionType.Call, 'strike': 105, 'v': 0.19 }, { 'type': OptionType.Call, 'strike': 110, 'v': 0.18 }, { 'type': OptionType.Call, 'strike': 115, 'v': 0.17 }, { 'type': OptionType.Call, 'strike': 120, 'v': 0.16 }, { 'type': OptionType.Call, 'strike': 125, 'v': 0.15 }, { 'type': OptionType.Call, 'strike': 130, 'v': 0.14 }, { 'type': OptionType.Call, 'strike': 135, 'v': 0.13 }, ] dates = [self.ex_date] call_strikes, put_strikes, call_vols, put_vols = [], [], [], [] # Assumes ascending strikes and same min call and max put strikes for data in replicating_option_data: if data['type'] == OptionType.Call: call_strikes.append(data['strike']) call_vols.append(data['v']) elif data['type'] == OptionType.Put: put_strikes.append(data['strike']) put_vols.append(data['v']) else: raise ValueError("unknown option type") vols = np.zeros((len(replicating_option_data) - 1, 1)) strikes = [] for j, v in enumerate(put_vols): vols[j][0] = v strikes.append(put_strikes[j]) for k in range(1, len(call_vols)): j = len(put_vols) - 1 vols[j + k][0] = call_vols[k] strikes.append(call_strikes[k]) vols_mat = Matrix.from_ndarray(vols) vol_ts = BlackVarianceSurface(self.today, NullCalendar(), dates, strikes, vols_mat, self.dc) stoch_process = BlackScholesMertonProcess(self.spot, self.q_ts, self.r_ts, vol_ts) engine = ReplicatingVarianceSwapEngine(stoch_process, call_strikes, put_strikes, 5.0) variance_swap = VarianceSwap( self.values['type'], self.values['strike'], self.values['nominal'], self.today, self.ex_date, ) variance_swap.set_pricing_engine(engine) calculated = variance_swap.variance expected = self.values['result'] self.assertAlmostEqual(calculated, expected, delta=self.values['tol'])