def __init__(self, option, grid=None, spot_max=1500.0, nspots=100, spotdensity=7.0, force_exact=True, flip_idx_spot=False, schemes={}, cache=True, coefficients=None, boundaries=None, force_bandwidth=False, logspace=False, verbose=True ): """@option@ is a BlackScholesOption""" self.cache = cache assert isinstance(option, Option) self.option = option if logspace: def mu_s(t, *dim): return option.interest_rate.value - 0.5*option.variance.value def gamma2_s(t, *dim): return 0.5 * option.variance.value else: def mu_s(t, *dim): return option.interest_rate.value * dim[0] def gamma2_s(t, *dim): return 0.5 * option.variance.value * dim[0]**2 if grid: self.grid = grid spots = self.grid.mesh[0] else: if logspace: dx = (np.log(spot_max) - np.log(option.spot)) / (nspots//2) spots = np.log(option.spot) + dx*np.linspace(-(nspots//2), nspots//2, nspots) init = lambda x: np.maximum(np.exp(x) - option.strike, 0) self.spots = np.exp(spots) else: spots = utils.sinh_space(option.spot, spot_max, spotdensity, nspots, force_exact=force_exact) self.spots = spots init = lambda x: np.maximum(x - option.strike, 0) grid = Grid([spots], initializer=init) # spot = self.spots[self.idx] # self.option = BlackScholesOption(spot=np.exp(spot), strike=k, interest_rate=r, # variance=v, tenor=t) # G = Grid([spots], initializer=lambda *x: np.maximum(np.exp(x[0])-option.strike,0)) if not coefficients: coefficients = {() : lambda t: -option.interest_rate.value, (0,) : mu_s, (0,0): gamma2_s} if not boundaries: if logspace: boundaries = { # D: U = 0 Von Neumann: dU/dS = 1 (0,) : ((0, lambda *args: 0.0), (1, lambda t, x: np.exp(x))), # D: U = 0 Free boundary (0,0) : ((0, lambda *args: 0.0), (None, lambda t, x: np.exp(x)))} else: boundaries = { # D: U = 0 Von Neumann: dU/dS = 1 (0,) : ((0, lambda *args: 0.0), (1, lambda t, x: 1.0)), # D: U = 0 Free boundary (0,0) : ((0, lambda *args: 0.0), (None, lambda t, x: 1.0))} FDE.FiniteDifferenceEngineADI.__init__(self, grid, coefficients=coefficients, boundaries=boundaries, schemes=schemes, force_bandwidth=force_bandwidth)
rho = 0 # Grid parameters rate_Spot_Var = 0.5 # Proportion to solve in the var step spot_max = 1500.0 var_max = 13.0 nspots = 100 nvols = 100 spotdensity = 7.0 # infinity is linear? varexp = 4 spots = utils.sinh_space(k, spot_max, spotdensity, nspots) spot = spots[min(abs(spots - spot)) == abs(spots - spot)][0] k = spots[min(abs(spots - k)) == abs(spots - k)][0] vars = utils.exponential_space(0.00, v0, var_max, varexp, nvols) # vars = [v0] # spots = linspace(0.0, spot_max, nspots) # vars = linspace(0.0, var_max, nvols) # plot(spots); title("Spots"); show() # plot(vars); title("Vars"); show() trims = (k * .2 < spots) & (spots < k * 2.0) trimv = (0.01 < vars) & (vars < 1) # v0*2.0) trims = slice(None) trimv = slice(None) # Does better without upwinding here
, mean_reversion = 1 , mean_variance = 0.04 , vol_of_variance = 0.4 , correlation = 0.3 ) spot_max = 1500.0 var_max = 13.0 nspots = 100 nvols = 100 spotdensity = 7.0 # infinity is linear? varexp = 4 spots = utils.sinh_space(H.strike, spot_max, spotdensity, nspots) vars = utils.exponential_space(0.00, H.variance.value, var_max, varexp, nvols) # vars = [v0] # spots = np.linspace(0.0, spot_max, nspots) # vars = np.linspace(0.0, var_max, nvols) # plot(spots); title("Spots"); show() # plot(vars); title("Vars"); show() H.strike = spots[min(abs(spots - H.strike)) == abs(spots - H.strike)][0] H.spot = spots[min(abs(spots - H.spot)) == abs(spots - H.spot)][0] def init(spots, vars): return np.maximum(0, spots - H.strike) Gi = Grid(mesh=(spots, vars), initializer=init) G = Gi.copy() V_init = G.domain.copy()
def __init__(self, option, grid=None, spot_max=1500.0, nspots=100, spotdensity=7.0, force_exact=True, flip_idx_spot=False, schemes={}, cache=True, coefficients=None, boundaries=None, force_bandwidth=False, logspace=False, verbose=True): """@option@ is a BlackScholesOption""" self.cache = cache assert isinstance(option, Option) self.option = option if logspace: def mu_s(t, *dim): return option.interest_rate.value - 0.5 * option.variance.value def gamma2_s(t, *dim): return 0.5 * option.variance.value else: def mu_s(t, *dim): return option.interest_rate.value * dim[0] def gamma2_s(t, *dim): return 0.5 * option.variance.value * dim[0]**2 if grid: self.grid = grid spots = self.grid.mesh[0] else: if logspace: dx = (np.log(spot_max) - np.log(option.spot)) / (nspots // 2) spots = np.log(option.spot) + dx * np.linspace( -(nspots // 2), nspots // 2, nspots) init = lambda x: np.maximum(np.exp(x) - option.strike, 0) self.spots = np.exp(spots) else: spots = utils.sinh_space(option.spot, spot_max, spotdensity, nspots, force_exact=force_exact) self.spots = spots init = lambda x: np.maximum(x - option.strike, 0) grid = Grid([spots], initializer=init) # spot = self.spots[self.idx] # self.option = BlackScholesOption(spot=np.exp(spot), strike=k, interest_rate=r, # variance=v, tenor=t) # G = Grid([spots], initializer=lambda *x: np.maximum(np.exp(x[0])-option.strike,0)) if not coefficients: coefficients = { (): lambda t: -option.interest_rate.value, (0, ): mu_s, (0, 0): gamma2_s } if not boundaries: if logspace: boundaries = { # D: U = 0 Von Neumann: dU/dS = 1 (0, ): ((0, lambda *args: 0.0), (1, lambda t, x: np.exp(x))), # D: U = 0 Free boundary (0, 0): ((0, lambda *args: 0.0), (None, lambda t, x: np.exp(x))) } else: boundaries = { # D: U = 0 Von Neumann: dU/dS = 1 (0, ): ((0, lambda *args: 0.0), (1, lambda t, x: 1.0)), # D: U = 0 Free boundary (0, 0): ((0, lambda *args: 0.0), (None, lambda t, x: 1.0)) } FDE.FiniteDifferenceEngineADI.__init__(self, grid, coefficients=coefficients, boundaries=boundaries, schemes=schemes, force_bandwidth=force_bandwidth)
def __init__(self, option, grid=None, spot_max=1500.0, spot_min=0.0, spots=None, vars=None, var_max=10.0, nspots=100, nvols=100, spotdensity=7.0, varexp=4.0, force_exact=True, flip_idx_var=False, flip_idx_spot=False, schemes=None, coefficients=None, boundaries=None, cache=True, verbose=True, force_bandwidth=None ): """@option@ is a HestonOption""" self.cache = cache assert isinstance(option, Option) self.option = option if not coefficients: def mu_s(t, *dim): # return option.interest_rate.value - 0.5 * dim[1] return option.interest_rate.value * dim[0] def gamma2_s(t, *dim): # return 0.5 * dim[1] return 0.5 * dim[1] * dim[0]**2 def mu_v(t, *dim): if np.isscalar(dim[0]): if dim[0] == 0: return 0 ret = option.variance.reversion * (option.variance.mean - dim[1]) ret[dim[0]==0] = 0 return ret def gamma2_v(t, *dim): if np.isscalar(dim[0]): if dim[0] == 0: return 0 ret = 0.5 * option.variance.volatility**2 * dim[1] ret[dim[0]==0] = 0 return ret def cross(t, *dim): # return option.correlation * option.variance.volatility * dim[1] return option.correlation * option.variance.volatility * dim[0] * dim[1] coefficients = {() : lambda t: -option.interest_rate.value, (0,) : mu_s, (0,0): gamma2_s, (1,) : mu_v, (1,1): gamma2_v, (0,1): cross, } if not boundaries: boundaries = { # D: U = 0 VN: dU/dS = 1 # (0,) : ((0, lambda t, *dim: 0.0), (1, lambda t, *dim: np.exp(dim[0]))), (0,) : ((0, lambda t, *dim: 0.0), (1, lambda t, *dim: 1.0)), # D: U = 0 Free boundary # (0,0) : ((0, lambda t, *dim: 0.0), (None, lambda t, *dim: np.exp(dim[0]))), (0,0) : ((0, lambda t, *dim: 0.0), (None, lambda t, *dim: 1.0)), # Free boundary at low variance (1,) : ((None, lambda t, *dim: None), # # D intrinsic value at high variance # (0, lambda t, *dim: np.exp(-option.interest_rate.value * t) * dim[0]) (None, lambda t, *dim: None) # (0, lambda t, *dim: dim[0]) ), # We know from the PDE that this will be 0 because # the vol is 0 at the low boundary (1,1) : ((1, lambda t, *dim: 0), # D intrinsic value at high variance # (0, lambda t, *dim: np.exp(-option.interest_rate.value * t) * np.maximum(0.0, np.exp(dim[0])-option.strike))), (None, lambda t, *dim: None) # (0, lambda t, *dim: dim[0]) # (0, lambda t, *dim: 0) ) } if isinstance(option, BarrierOption): if option.top: if option.top[0]: # Knockin, not sure about implementing this raise NotImplementedError("Knockin barriers are not supported.") else: spot_max = option.top[1] if grid: assert np.allclose(spot_max, max(grid.mesh[0])) boundaries[(0,)] = (boundaries[(0,)][0], (0, lambda *x: 0.0)) boundaries[(0,0)] = boundaries[(0,)] if option.bottom: if option.bottom[0]: # Knockin, not sure about implementing this raise NotImplementedError("Knockin barriers are not supported.") else: spot_min = option.bottom[1] boundaries[(0,)] = ((0, lambda *x: 0.0), boundaries[(0,)][1]) boundaries[(0,0)] = boundaries[(0,)] if grid: self.spots = grid.mesh[0] self.vars = grid.mesh[1] else: if vars is None: # vars = np.linspace(0, var_max, nvols) vars = utils.exponential_space(0.00, option.variance.value, var_max, varexp, nvols, force_exact=force_exact) self.vars = vars if spots is None: # spots = np.linspace(0,spot_max,nspots) if isinstance(option, BarrierOption) and option.top and not option.top[0]: p = 3 # spots = np.linspace(0, spot_max**p, nspots)**(1.0/p) spots = utils.exponential_space(0.00, self.option.strike, spot_max, 1.0/p, nspots, force_exact=force_exact) print "Barrier spots" else: spots = utils.sinh_space(option.strike-spot_min, spot_max-spot_min, spotdensity, nspots, force_exact=force_exact) + spot_min self.spots = spots grid = Grid([self.spots, self.vars], initializer=lambda *x: np.maximum(x[0]-option.strike,0)) newstrike = self.spots[np.argmin(np.abs(self.spots - option.strike))] self.spots[np.argmin(np.abs(self.spots - option.spot))] = option.spot # if newstrike != option.strike: # print "Strike %s -> %s" % (option.strike, newstrike) # option.strike = newstrike # if newspot != option.spot: # print "Spot %s -> %s" % (option.spot, newspot) # option.spot = newspot if flip_idx_var is True: # Need explicit boolean True flip_idx_var = bisect_left( np.round(self.vars, decimals=5), np.round(option.variance.mean, decimals=5)) if flip_idx_spot is True: # Need explicit boolean True flip_idx_spot = bisect_left( np.round(self.spots, decimals=5), np.round(option.strike, decimals=5)) if schemes is None: schemes = {} else: schemes = {k : list(v) for k, v in schemes.items()} # for k,v in new.items(): # assert schemes[k] is not v # schemes = new if (0,) not in schemes: schemes[(0,)] = [{"scheme": "center"}] if flip_idx_spot is not False: schemes[(0,)].append({"scheme": 'forward', "from" : flip_idx_spot}) if (1,) not in schemes: schemes[(1,)] = [{"scheme": "center"}] if flip_idx_var is not False: schemes[(1,)].append({"scheme": 'backward', "from" : flip_idx_var}) if verbose: print "(0,): Start with %s differencing." % (schemes[(0,)][0]['scheme'],) if len(schemes[(0,)]) > 1: print "(0,): Switch to %s differencing at %i." % (schemes[(0,)][1]['scheme'], schemes[(0,)][1]['from']) print "(1,): Start with %s differencing." % (schemes[(1,)][0]['scheme'],) if len(schemes[(1,)]) > 1: print "(1,): Switch to %s differencing at %i." % (schemes[(1,)][1]['scheme'], schemes[(1,)][1]['from']) self.grid = grid self.coefficients = coefficients self.boundaries = boundaries self.schemes = schemes self.force_bandwidth = force_bandwidth self._initialized = False
sigma = 0.4 rho = 0 # Grid parameters rate_Spot_Var = 0.5 # Proportion to solve in the var step spot_max = 1500.0 var_max = 13.0 nspots = 100 nvols = 100 spotdensity = 7.0 # infinity is linear? varexp = 4 spots = utils.sinh_space(k, spot_max, spotdensity, nspots) spot = spots[min(abs(spots - spot)) == abs(spots - spot)][0] k = spots[min(abs(spots - k)) == abs(spots - k)][0] vars = utils.exponential_space(0.00, v0, var_max, varexp, nvols) # vars = [v0] # spots = linspace(0.0, spot_max, nspots) # vars = linspace(0.0, var_max, nvols) # plot(spots); title("Spots"); show() # plot(vars); title("Vars"); show() trims = (k * .2 < spots) & (spots < k * 2.0) trimv = (0.01 < vars) & (vars < 1) # v0*2.0) trims = slice(None) trimv = slice(None) # Does better without upwinding here