def testCalibration(self): m1 = (self.beta + (self.alpha + self.gamma * ql.CumulativeNormalDistribution() (self.lambda_value)) * (1 + self.lambda_value * self.lambda_value) + self.gamma * self.lambda_value * exp(-self.lambda_value * self.lambda_value / 2) / sqrt(2 * pi)) v0 = self.omega / (1 - m1) helpers = list() for s in range(3, 10): for m in range(0, 3): vol = ql.QuoteHandle(ql.SimpleQuote(self.Volatility[s * 8 + m])) maturity = ql.Period(int((self.days[m + 1] + 3) / 7), ql.Weeks) heston_helper = ql.HestonModelHelper( maturity, self.calendar, self.s0, self.strike[s], vol, self.risk_free_ts, self.dividend_yield_handle, ql.BlackCalibrationHelper.ImpliedVolError, ) helpers.append(heston_helper) new_garch_process = ql.GJRGARCHProcess( self.risk_free_ts, self.dividend_yield_handle, ql.QuoteHandle(ql.SimpleQuote(self.s0)), v0, self.omega, self.alpha, self.beta, self.gamma, self.lambda_value, self.daysPerYear, ) new_garch_model = ql.GJRGARCHModel(new_garch_process) new_garch_engine = ql.AnalyticGJRGARCHEngine(new_garch_model) for helper in helpers: helper.setPricingEngine(new_garch_engine) om = ql.Simplex(0.05) new_garch_model.calibrate( helpers, om, ql.EndCriteria(400, 40, 1.0e-8, 1.0e-8, 1.0e-8)) sse = 0 for helper in helpers: diff = helper.calibrationError() * 100 sse += diff * diff maxExpected = 15 self.assertTrue(sse <= maxExpected)
def testOptionPricing(self): tolerance = 0.075 for k in range(3): lambda_value = self.lambda_values[k] m1 = (self.beta + (self.alpha + self.gamma * ql.CumulativeNormalDistribution() (lambda_value)) * (1 + lambda_value * lambda_value) + self.gamma * lambda_value * exp(-lambda_value * lambda_value / 2) / sqrt(2 * pi)) v0 = self.omega / (1 - m1) quote = ql.QuoteHandle(ql.SimpleQuote(self.s0)) garch = ql.GJRGARCHProcess( self.risk_free_handle, self.dividend_yield_handle, quote, v0, self.omega, self.alpha, self.beta, self.gamma, lambda_value, self.daysPerYear, ) garch_model = ql.GJRGARCHModel(garch) analytic_engine = ql.AnalyticGJRGARCHEngine(garch_model) mc_engine = ql.MCEuropeanGJRGARCHEngine(process=garch, traits="pseudorandom", timeStepsPerYear=20, requiredTolerance=0.02, seed=1234) for i in range(2): for j in range(6): payoff = ql.PlainVanillaPayoff(ql.Option.Call, self.strike[j]) ex_date = self.settle_date + ql.Period( self.maturity[i], ql.Days) exercise = ql.EuropeanExercise(ex_date) option = ql.VanillaOption(payoff, exercise) option.setPricingEngine(analytic_engine) analytic_price = option.NPV() analytic_difference = analytic_price - self.analytic[k][i][ j] self.assertTrue(analytic_difference <= 2 * tolerance) option.setPricingEngine(mc_engine) mc_price = option.NPV() mc_difference = mc_price - self.mc_values[k][i][j] self.assertTrue(mc_difference <= 2 * tolerance)
def to_ql_option_engine(engine_name=None, process=None, model=None): """ Returns a QuantLib.PricingEngine for Options :param engine_name: str The engine name :param process: QuantLib.StochasticProcess The QuantLib object with the option Stochastic Process. :param model: QuantLib.CalibratedModel :return: QuantLib.PricingEngine """ if engine_name.upper() == 'BINOMIAL_VANILLA': return ql.BinomialVanillaEngine(process, 'LR', 801) elif engine_name.upper() == 'ANALYTIC_HESTON': if model is None: model = ql.HestonModel(process) elif model is not None and process is not None: model = model(process) return ql.AnalyticHestonEngine(model) elif engine_name.upper() == 'ANALYTIC_EUROPEAN': return ql.AnalyticEuropeanEngine(process) elif engine_name.upper() == 'ANALYTIC_EUROPEAN_DIVIDEND': return ql.AnalyticDividendEuropeanEngine(process) elif engine_name.upper() == "FINITE_DIFFERENCES": return ql.FdBlackScholesVanillaEngine(process) elif engine_name.upper() == 'HESTON_FINITE_DIFFERENCES': if model is None: model = ql.HestonModel(process) elif model is not None and process is not None: model = model(process) return ql.FdHestonVanillaEngine(model) elif engine_name.upper() == "BARONE_ADESI_WHALEY": return ql.BaroneAdesiWhaleyEngine(process) elif engine_name.upper() == "BJERKSUND_STENSLAND": return ql.BjerksundStenslandEngine(process) elif engine_name.upper() == "ANALYTIC_GJR_GARCH": if model is None: model = ql.GJRGARCHModel(process) elif model is not None and process is not None: model = model(process) return ql.AnalyticGJRGARCHEngine(model) elif engine_name.upper() == 'MC_GJR_GARCH': return ql.MCEuropeanGJRGARCHEngine(process=process, traits='pseudorandom', timeStepsPerYear=20, requiredTolerance=0.02) else: return None