import QuantLib as ql def build_calibrated_hw( curve_handle, swaption_helpers, a_init=0.05, sigma_init=0.01 ): hw = ql.HullWhite(curve_handle, a_init, sigma_init) optimizer = ql.LevenbergMarquardt() end_criteria = ql.EndCriteria(1000, 500, 1e-8, 1e-8, 1e-8) hw.calibrate(swaption_helpers, optimizer, end_criteria) return hw import numpy as np def simulate_hw_state( hw: ql.HullWhite, curve_handle, time_grid, n_paths, seed=42 ): a, sigma = hw.params() dt = np.diff(time_grid) n_steps = len(time_grid) np.random.seed(seed) x = np.zeros((n_paths, n_steps)) for i in range(1, n_steps): decay = np.exp(-a * dt[i-1]) vol = sigma * np.sqrt((1 - np.exp(-2 * a * dt[i-1])) / (2 * a)) z = np.random.normal(size=n_paths) x[:, i] = x[:, i-1] * decay + vol * z return x def swap_npv_from_state( swap, hw, curve_handle, t, x_t, today ): ql.Settings.instance().evaluationDate = today + int(t * 365) hw.setState(x_t) engine = ql.DiscountingSwapEngine( curve_handle, False, hw ) swap.setPricingEngine(engine) return swap.NPV()