def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() M = len(se.time_grid) I = self.paths paths = np.zeros((M, I)) paths[0] = self.initial_value if self.correlated is False: sn1 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: sn1 = self.random_numbers sn2 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) rj = self.lamb * (np.exp(self.mu + 0.5 * self.delt**2) - 1) short_rate = self.discount_curve.short_rate for t in range(1, len(self.time_grid)): if self.correated in False: ran = sn1(t) else: ran = np.dot(self.cholesky_matrix, sn1[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count poi = np.random.poisson(self.lamb * dt, I) paths[t] = paths[t - 1] + ( np.exp((short_rate - rj - 0.5 * self.volatility**2) * dt + self.volatility * np.sqrt(dt) * ran) + (np.exp(self.mu + self.delt * sn2[t]) - 1) * poi) self.instrument_value = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: # method from generic simulation class self.generate_time_grid() # number of dates for time grid M = len(self.time_grid) # number of paths I = self.paths # ndarray initialization for path simulation paths = np.zeros((M, I)) # initialize first date with initial_value paths[0] = self.initial_value if self.correlated is False: # if not correlated, generate random numbers sn1 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: # if correlated, use random number object as provided # in market environment sn1 = self.random_numbers # standard normally distributed pseudorandom numbers # for the jump component sn2 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) rj = self.lamb * (np.exp(self.mu + 0.5 * self.delt ** 2) - 1) short_rate = self.discount_curve.short_rate for t in range(1, len(self.time_grid)): # select the right time slice from the relevant # random number set if self.correlated is False: ran = sn1[t] else: # only with correlation in portfolio context ran = np.dot(self.cholesky_matrix, sn1[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count # difference between two dates as year fraction poi = np.random.poisson(self.lamb * dt, I) # Poisson-distributed pseudorandom numbers for jump component paths[t] = paths[t - 1] * ( np.exp((short_rate - rj - 0.5 * self.volatility ** 2) * dt + self.volatility * np.sqrt(dt) * ran) + (np.exp(self.mu + self.delt * sn2[t]) - 1) * poi) self.instrument_values = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() # method from generic simulation class # number of dates for time grid M = len(self.time_grid) # number of paths I = self.paths # array initialization for path simulation paths = np.zeros((M, I)) # initialize first date with initial_value paths[0] = self.initial_value if self.correlated is False: # if not correlated, generate random numbers sn1 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: # if correlated, use random number object as provided # in market environment sn1 = self.random_numbers # standard normally distributed pseudorandom numbers # for the jump component sn2 = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) rj = self.lamb * (np.exp(self.mu + 0.5 * self.delt ** 2) - 1) short_rate = self.discount_curve.short_rate for t in range(1, len(self.time_grid)): # select the right time slice from the relevant # random number set if self.correlated is False: ran = sn1[t] else: # only with correlation in portfolio context ran = np.dot(self.cholesky_matrix, sn1[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count # difference between two dates as year fraction poi = np.random.poisson(self.lamb * dt, I) # Poisson-distributed pseudorandom numbers for jump component paths[t] = paths[t - 1] * (np.exp((short_rate - rj - 0.5 * self.volatility ** 2) * dt + self.volatility * np.sqrt(dt) * ran) + (np.exp(self.mu + self.delt * sn2[t]) - 1) * poi) self.instrument_values = paths
def generate_paths(self, fixed_seed=True, day_count=365.): if self.time_grid is None: self.generate_time_grid() M = len(self.time_grid) I = self.paths paths = np.zeros((M, I)) paths_ = np.zeros_like(paths) paths[0] = self.initial_value paths_[0] = self.initial_value if self.correlated is False: rand = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: rand = self.random_numbers for t in range(1, len(self.time_grid)): dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count if self.correlated is False: ran = rand[t] else: ran = np.dot(self.cholesky_matrix, rand[:, t, :]) ran = ran[self.rn_set] # full truncation Euler discretization paths_[t] = (paths_[t - 1] + self.kappa * (self.theta - np.maximum(0, paths_[t - 1, :])) * dt + np.sqrt(np.maximum(0, paths_[t - 1, :])) * self.volatility * np.sqrt(dt) * ran) paths[t] = np.maximum(0, paths_[t]) self.instrument_values = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() num_dates = len(self.time_grid) num_paths = self.paths paths_1 = np.zeros((num_dates, num_paths)) paths_2 = np.zeros_like(paths_1) paths_1[0] = self.initial_value paths_2[0] = self.initial_value # decide the correct random numbers basted correlation if self.correlated is False: sn = sn_random_numbers((1, num_dates, num_paths), fixed_seed=fixed_seed) else: sn = self.random_numbers # Generate paths: for t in range(1, len(self.time_grid)): # time interval dt = (self.time_grid[t] - self.time_grid[t-1]).days / day_count # choose the correct random number if self.correlated is False: random_num = sn[t] else: random_num = np.dot(self.cholesky_matrix, sn[:, t, :]) random_num = random_num[t] # full trunctation with Euler Discretization paths_2[t] = (paths_2[t-1] + self.kappa * (self.theta - np.maximum(0, paths_2[t-1, :])) * dt + self.volatility * np.sqrt(np.maximum(0, paths_2[t-1, :])) * np.sqrt(dt) * random_num) paths_1[t] = np.maximum(0, paths_2[t]) self.instrument_values = paths_1
def generate_path(self, fixed_seed=False, day_count=365): if self.time_grid is None: self.generate_time_grid() M = len(self.time_grid) I = self.paths paths = np.zeros((M, I)) paths[0] = self.initial_value if not self.correlated: rand = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: rand = self.random_numbers short_rate = self.discount_curve.short_rate for t in range(1, len(self.time_grid)): if not self.correlated: ran = rand[t] else: ran = np.dot(self.cholesky_matrix, rand[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count paths[t] = paths[t - 1] * np.exp( (short_rate - 0.5 * self.volatility**2) * dt + self.volatility * np.sqrt(dt) * ran) self.instrument_values = paths
def generate_paths(self, fixed_seed=False,day_count=365.): if self.time_grid is None: self.generate_time_grid() # method from generic simulation class # number of dates for time grid M = len(self.time_grid) # number of paths I = self.paths # array initialization for path simulation paths = np.zeros((M, I)) paths_ = np.zeros_like(paths) # initialize first date with initial_value paths[0] = self.initial_value paths_[0] = self.initial_value if self.correlated is False: # if not correlated, generate random numbers rand = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: # if correlated, use random number object as provided rand = self.random_numbers for t in range(1, len(self.time_grid)): dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count if self.correlated is False: ran = rand[t] else: ran = np.dot(self.cholesky_matrix, rand[:, t, :]) ran = ran[self.rn_set] # full truncation Euler discretization paths_[t] = (paths_[t - 1] + self.kappa * (self.theta - np.maximum(0, paths_[t - 1, :])) * dt + np.sqrt(np.maximum(0, paths_[t - 1, :])) * self.volatility * np.sqrt(dt) * ran) paths[t] = np.maximum(0, paths_[t]) self.instrument_values = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() # method from generoc simulation class # number of dates for time grid M = len(self.time_grid) # number of paths I = self.paths # array initialization for path simulation paths = np.zeros((M, I)) # initialize first date with initial_value paths[0] = self.initial_value if not self.correlated: # if not correlated, generate random numbers # when shape[0] is 1, returns a 2 x 2 matrix rand = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: # if correlated, use random number object as provided # in market environment rand = self.random_numbers short_rate = self.discount_curve.short_rate # get short rate for drift of process for t in range(1, len(self.time_grid)): # select the right time slice from the relevant # random number set if not self.correlated: ran = rand[t] else: ran = np.dot(self.cholesky_matrix, rand[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count # difference between two dates as year fraction paths[t] = paths[t - 1] * np.exp((short_rate - 0.5 * self.volatility ** 2) * dt + self.volatility * np.sqrt(dt) * ran) # generate simulated values for the respective date self.instrument_values = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() # method from generic simulation class # number of dates for time grid M = len(self.time_grid) # number of paths I = self.paths # array initialization for path simulation paths = np.zeros((M, I)) # initialize first date with initial_value paths[0] = self.initial_value if not self.correlated: # if not correlated, generate random numbers rand = sn_random_numbers((1, M, I), fixed_seed=fixed_seed) else: # if correlated, use random number object as provided # in market environment rand = self.random_numbers short_rate = self.discount_curve.short_rate # get short rate for drift of process for t in range(1, len(self.time_grid)): # select the right time slice from the relevant # random number set if not self.correlated: ran = rand[t] else: ran = np.dot(self.cholesky_matrix, rand[:, t, :]) ran = ran[self.rn_set] dt = (self.time_grid[t] - self.time_grid[t - 1]).days / day_count # difference between two dates as year fraction paths[t] = paths[t - 1] * np.exp((short_rate - 0.5 * self.volatility ** 2) * dt + self.volatility * np.sqrt(dt) * ran) # generate simulated values for the respective date self.instrument_values = paths
def generate_paths(self, fixed_seed=False, day_count=365.): if self.time_grid is None: self.generate_time_grid() # initialization for path simulation, and initialize the first date num_dates = len(self.time_grid) num_paths = self.paths paths = np.zeros((num_dates, num_paths)) paths[0] = self.initial_value if not self.correlated: # if not correlated, generate random numbers random_nums = sn_random_numbers((1, num_dates, num_paths), fixed_seed=fixed_seed) else: # if correlated, use provided random numbers from mkt env random_nums = self.random_numbers # get the short rate for GBM drift short_rate = self.discount_curve.short_rate # generate the paths for t in range(1,len(self.time_grid)): # choose the correct time step # depending on if correalted if not self.correlated: rand = random_nums[t] else: rand = np.dot(self.cholesky_matrix, random_nums[:,t,:]) rand = rand(self.rn_set) # calculate dt: difference between two simulated dates in year's fraction dt = (self.time_grid[t] - self.time_grid[t-1]).days / day_count # generate the simulated values at the current time grid paths[t] = paths[t-1] * np.exp((short_rate - 0.5*self.volatility**2)*dt + self.volatility*np.sqrt(dt) * rand) self.instrument_values = paths
def __init__(self, name, positions, val_env, assets, correlations=None, fixed_seed=False): self.name = name self.positions = positions self.val_env = val_env self.assets = assets self.underlyings = set() self.correlations = correlations self.time_grid = None self.underlying_objects = {} self.valuation_objects = {} self.fixed_seed = fixed_seed self.special_dates = [] for pos in self.positions: # determine earliest starting_date self.val_env.constants['starting_date'] = \ min(self.val_env.constants['starting_date'], positions[pos].mar_env.pricing_date) # determine latest date of relevance self.val_env.constants['final_date'] = \ max(self.val_env.constants['final_date'], positions[pos].mar_env.constants['maturity']) # collect all underlyings # add to set; avoids redundancy self.underlyings.add(positions[pos].underlying) # generate general time grid start = self.val_env.constants['starting_date'] end = self.val_env.constants['final_date'] time_grid = pd.date_range( start=start, end=end, freq=self.val_env.constants['frequency']).to_pydatetime() time_grid = list(time_grid) for pos in self.positions: maturity_date = positions[pos].mar_env.constants['maturity'] if maturity_date not in time_grid: time_grid.insert(0, maturity_date) self.special_dates.append(maturity_date) if start not in time_grid: time_grid.insert(0, start) if end not in time_grid: time_grid.append(end) # delete duplicate entries time_grid = list(set(time_grid)) # sort dates in time_grid time_grid.sort() self.time_grid = np.array(time_grid) self.val_env.add_list('time_grid', self.time_grid) if correlations is not None: # take care of correlations ul_list = sorted(self.underlyings) correlation_matrix = np.zeros((len(ul_list), len(ul_list))) np.fill_diagonal(correlation_matrix, 1.0) correlation_matrix = pd.DataFrame(correlation_matrix, index=ul_list, columns=ul_list) for i, j, corr in correlations: corr = min(corr, 0.999999999999) # fill correlation matrix correlation_matrix.loc[i, j] = corr correlation_matrix.loc[j, i] = corr # determine Cholesky matrix cholesky_matrix = np.linalg.cholesky( np.array(correlation_matrix)) # dictionary with index positions for the # slice of the random number array to be used by # respective underlying rn_set = { asset: ul_list.index(asset) for asset in self.underlyings } # random numbers array, to be used by # all underlyings (if correlations exist) random_numbers = sn_random_numbers( (len(rn_set), len( self.time_grid), self.val_env.constants['paths']), fixed_seed=self.fixed_seed) # add all to valuation environment that is # to be shared with every underlying self.val_env.add_list('cholesky_matrix', cholesky_matrix) self.val_env.add_list('random_numbers', random_numbers) self.val_env.add_list('rn_set', rn_set) for asset in self.underlyings: # select market environment of asset mar_env = self.assets[asset] # add valuation environment to market environment mar_env.add_environment(val_env) # select right simulation class model = models[mar_env.constants['model']] # instantiate simulation object if correlations is not None: self.underlying_objects[asset] = model(asset, mar_env, corr=True) else: self.underlying_objects[asset] = model(asset, mar_env, corr=False) for pos in positions: # select right valuation class (European, American) val_class = otypes[positions[pos].otype] # pick market environment and add valuation environment mar_env = positions[pos].mar_env mar_env.add_environment(self.val_env) # instantiate valuation class self.valuation_objects[pos] = \ val_class(name=positions[pos].name, mar_env=mar_env, underlying=self.underlying_objects[ positions[pos].underlying], payoff_func=positions[pos].payoff_func)