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 setUp(self): self.settings = Settings() self.calendar = TARGET() self.todays_date = Date(15, May, 1998) self.settlement_date = Date(17, May, 1998) self.settings.evaluation_date = self.todays_date # options parameters self.option_type = Put self.underlying = 36 self.strike = 40 self.dividend_yield = 0.00 self.risk_free_rate = 0.06 self.volatility = 0.20 self.maturity = Date(17, May, 1999) self.daycounter = Actual365Fixed() self.underlyingH = SimpleQuote(self.underlying) # bootstrap the yield/dividend/vol curves self.flat_term_structure = FlatForward( reference_date=self.settlement_date, forward=self.risk_free_rate, daycounter=self.daycounter) self.flat_dividend_ts = FlatForward( reference_date=self.settlement_date, forward=self.dividend_yield, daycounter=self.daycounter) self.flat_vol_ts = BlackConstantVol(self.settlement_date, self.calendar, self.volatility, self.daycounter) self.black_scholes_merton_process = BlackScholesMertonProcess( self.underlyingH, self.flat_dividend_ts, self.flat_term_structure, self.flat_vol_ts) self.payoff = PlainVanillaPayoff(self.option_type, self.strike) #Additional parameters for testing DividendVanillaOption self.dividend_dates = [] self.dividends = [] self.american_time_steps = 600 self.american_grid_points = 600 #Parameters for implied volatility: self.accuracy = 0.001 self.max_evaluations = 1000 self.min_vol = 0.001 self.max_vol = 4 self.target_price = 4.485992
def setUp(self): self.settings = Settings() self.calendar = NullCalendar() self.today = Date(6, June, 2021) self.settlement_date = self.today + 90 self.settings.evaluation_date = self.today # options parameters self.option_type = Put self.underlying = 80.0 self.strike = 85.0 self.dividend_yield = -0.03 self.risk_free_rate = 0.05 self.volatility = 0.20 # self.maturity = Date(17, May, 1999) self.daycounter = Actual360() self.underlyingH = SimpleQuote(self.underlying) # bootstrap the yield/dividend/vol curves self.flat_term_structure = FlatForward( reference_date=self.today, forward=self.risk_free_rate, daycounter=self.daycounter ) self.flat_dividend_ts = FlatForward( reference_date=self.today, forward=self.dividend_yield, daycounter=self.daycounter ) self.flat_vol_ts = BlackConstantVol( self.today, self.calendar, self.volatility, self.daycounter ) self.black_scholes_merton_process = BlackScholesMertonProcess( self.underlyingH, self.flat_dividend_ts, self.flat_term_structure, self.flat_vol_ts ) self.payoff = PlainVanillaPayoff(self.option_type, self.strike)
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 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_analytic_cont_geo_av_price_greeks(self): tolerance = {} tolerance["delta"] = 1.0e-5 tolerance["gamma"] = 1.0e-5 # tolerance["theta"] = 1.0e-5 tolerance["rho"] = 1.0e-5 tolerance["divRho"] = 1.0e-5 tolerance["vega"] = 1.0e-5 opt_types = [Call, Put] underlyings = [100.0] strikes = [90.0, 100.0, 110.0] q_rates = [0.04, 0.05, 0.06] r_rates = [0.01, 0.05, 0.15] lengths = [1, 2] vols = [0.11, 0.50, 1.20] spot = SimpleQuote(0.0) q_rate = SimpleQuote(0.0) r_rate = SimpleQuote(0.0) vol = SimpleQuote(0.0) q_ts = flat_rate(q_rate, self.daycounter) r_ts = flat_rate(r_rate, self.daycounter) vol_ts = BlackConstantVol(self.today, self.calendar, vol, self.daycounter) process = BlackScholesMertonProcess(spot, q_ts, r_ts, vol_ts) calculated = {} expected = {} for opt_type, strike, length in product(opt_types, strikes, lengths): maturity = EuropeanExercise(self.today + length*Years) payoff = PlainVanillaPayoff(opt_type, strike) engine = AnalyticContinuousGeometricAveragePriceAsianEngine(process) option = ContinuousAveragingAsianOption(Geometric, payoff, maturity) option.set_pricing_engine(engine) for u, m, n, v in product(underlyings, q_rates, r_rates, vols): q = m r = n spot.value = u q_rate.value = q r_rate.value = r vol.value = v value = option.npv calculated["delta"] = option.delta calculated["gamma"] = option.gamma # calculated["theta"] = option.theta calculated["rho"] = option.rho calculated["divRho"] = option.dividend_rho calculated["vega"] = option.vega if (value > spot.value*1.0e-5): # perturb spot and get delta and gamma du = u*1.0e-4 spot.value = u + du value_p = option.npv delta_p = option.delta spot.value = u - du value_m = option.npv delta_m = option.delta spot.value = u expected["delta"] = (value_p - value_m)/(2*du) expected["gamma"] = (delta_p - delta_m)/(2*du) # perturb rates and get rho and dividend rho dr = r*1.0e-4 r_rate.value = r + dr value_p = option.npv r_rate.value = r - dr value_m = option.npv r_rate.value = r expected["rho"] = (value_p - value_m)/(2*dr) dq = q*1.0e-4 q_rate.value = q + dq value_p = option.npv q_rate.value = q - dq value_m = option.npv q_rate.value = q expected["divRho"] = (value_p - value_m)/(2*dq) # perturb volatility and get vega dv = v*1.0e-4 vol.value = v + dv value_p = option.npv vol.value = v - dv value_m = option.npv vol.value = v expected["vega"] = (value_p - value_m)/(2*dv) # perturb date and get theta dt = self.daycounter.year_fraction(self.today - 1, self.today + 1) self.settings.evaluation_date = self.today - 1 value_m = option.npv self.settings.evaluation_date = self.today + 1 value_p = option.npv self.settings.evaluation_date = self.today expected["theta"] = (value_p - value_m)/dt # compare for greek, calcl in calculated.items(): expct = expected[greek] tol = tolerance[greek] error = relative_error(expct, calcl, u) self.assertTrue(error < tol)
volatility = 0.20 maturity = settlement_date + 363 daycounter = Actual365Fixed() underlyingH = SimpleQuote(underlying) # bootstrap the yield/dividend/vol curves flat_term_structure = FlatForward(reference_date=settlement_date, forward=risk_free_rate, daycounter=daycounter) flat_dividend_ts = FlatForward(reference_date=settlement_date, forward=dividend_yield, daycounter=daycounter) flat_vol_ts = BlackConstantVol(settlement_date, calendar, volatility, daycounter) black_scholes_merton_process = BlackScholesMertonProcess( underlyingH, flat_dividend_ts, flat_term_structure, flat_vol_ts) payoff = PlainVanillaPayoff(option_type, strike) european_exercise = EuropeanExercise(maturity) european_option = VanillaOption(payoff, european_exercise) method = 'Black-Scholes' analytic_european_engine = AnalyticEuropeanEngine(black_scholes_merton_process) european_option.set_pricing_engine(analytic_european_engine)