def iter_minimizer(Loss, beta_init, loss_args, bounds, max_deal_span, **kwargs): i = 0.1 def callback_print(xk): loss = Loss(xk, df=loss_args[0], coupons_cf=loss_args[1], streak_data=loss_args[2], weight_scheme=loss_args[4]) print(xk, loss) #optimization goes in loop until spot rate function will not be bigger #than 0 at each point; #Z constructs from 0.001 year to 30 year (no bonds with tenor > 30 year) while (Z(np.linspace(0.001, 30, 1000), beta_init)).min() < 0 or i == 0.1: res = minimize(fun=Loss, x0=beta_init, args=loss_args, bounds=bounds, callback=callback_print, **kwargs) beta_init = res.x #bounds of teta increases at every iteration by i which itself increases #also, as we want to exit negative zone as fast as we can bounds = ((0, 1), (None, None), (None, None), (beta_init[-1] + i, 0.05 * max_deal_span)) i += 0.1 return res
def naive_yield_Loss(beta, df, coupons_cf, streak_data, rho=0.2, weight_scheme='no_weight', tau=None): ''' Parameters --------------- beta: array-like Nelson-Siegel's vector of parameters df: Pandas Dataframe Dataframe of bonds' data coupons_cf: Pandas Dataframe Dataframe containing bonds' payment cash flows streak_data: Pandas Dataframe Dataframe containing bonds' payment calendar rho: float from 0 to 1, default 0.2 Weight of oldest deal - only used in 'vol_time', 'full_vol_time', 'volume_kzt', 'complex_volume' weight schemes weight_scheme: str, default 'no_weight' weight function used to weight deals tau: float, default None Use this parameter if only you do 3-variablie minimization. Parameter is only used in grid search optimization ''' #if tau is given, then beta is array if tau is not None: assert beta.shape[0] == 3 beta = np.append(beta, [tau]) #estimating price #calculatting Loss W = weight(beta, df=df, rho=rho, weight_scheme=weight_scheme) Loss = (W * np.square(df.ytm.values - Z(df.span / 365, beta))).sum() return Loss
def grad(beta, data, coupons_cf, streak_data, rho=0.2, weight_scheme='no_weight'): m = data['span'] grad_z = [] teta_m = m / beta[3] grad_z.append(1) grad_z.append((1 / teta_m) * (1 - np.exp(-teta_m))) grad_z.append((1 / teta_m) * (1 - np.exp(-teta_m)) - np.exp(-teta_m)) teta_m_der = m / (beta[3]**2) grad_z.append( ((beta[1] + beta[2]) * ((1 / m) * (1 - np.exp(-teta_m)) - np.exp(-teta_m) / beta[3]) - beta[2] * np.exp(-teta_m) * teta_m_der)) #calculatting Loss W = weight(beta, df=data, rho=rho, weight_scheme=weight_scheme) non_grad = W * (data['ytm'] / 100 - Z(m, beta)) loss_grad = np.zeros_like(beta) for i in range(beta.shape[0]): loss_grad[i] = -2 * (non_grad * grad_z[i]).sum() return loss_grad
def fixed_tau_minimizer(Loss, beta_init, loss_args, bounds, max_deal_span, **kwargs): i = 0.1 beta = beta_init.copy() beta.append(loss_args[5]) def callback_print(xk): loss = Loss(xk, df=loss_args[0], coupons_cf=loss_args[1], streak_data=loss_args[2], weight_scheme=loss_args[4], tau=loss_args[5]) print(xk, loss) #optimization goes in loop until spot rate function will not be bigger #than 0 at each point; #Z constructs from 0.001 year to 30 year (no bonds with tenor > 30 year) while (Z(np.linspace(0.001, 30, 1000), beta)).min() < 0 or i == 0.1: res = minimize(fun=Loss, x0=beta_init, args=loss_args, bounds=bounds, callback=callback_print, **kwargs) beta_init = res.x #if Z < 0 than tau will incremeantly increase until Z >= 0 #from tuple to list and back i += 1 loss_args = list(loss_args) loss_args[5] += i loss_args = tuple(loss_args) #beta change in order to asses new curve beta = beta_init.copy() beta = np.append(beta, loss_args[5]) return res
def area_boot(betas_array, tenors): spots_boot = pd.DataFrame() for i, beta_ in enumerate(betas_array): spots_boot[i] = Z(tenors, beta_) diff_curve = spots_boot.max(1) - spots_boot.min(1) area = np.trapz(diff_curve * 100, tenors) av_area = round(area / max(tenors), 3) return area, av_area
def filter_frame(self, loss_frame): accepted_ind = [] for ind in loss_frame.index: beta = loss_frame.loc[ind, loss_frame.columns[:-1]] spot_rate_curve = Z(self.maturities, beta) if (spot_rate_curve >= 0).all(): accepted_ind.append(ind) loss_frame_filtered = loss_frame.loc[accepted_ind, :] n_rows = loss_frame.shape[0] n_dropped_rows = n_rows - loss_frame_filtered.shape[0] print('{0} out of {1} of rows were dropped'.format( n_dropped_rows, n_rows)) return loss_frame_filtered
def filter_frame(self, loss_frame): accepted_ind = [] for ind in loss_frame.index: beta = loss_frame.loc[ind, loss_frame.columns[:-1]] spot_rate_curve = Z(self.maturities, beta) if (spot_rate_curve >= 0).all(): accepted_ind.append(ind) loss_frame_filtered = loss_frame.loc[accepted_ind, :] #printing info about № of dropped rows n_rows = loss_frame.shape[0] n_dropped_rows = n_rows - loss_frame_filtered.shape[0] self.logger.debug( f'{n_dropped_rows} out of {n_rows} of rows were dropped') return loss_frame_filtered
def iter_minimizer(Loss, beta_init, loss_args, bounds, max_deal_span, **kwargs): i = 0.1 def callback_print(xk): loss = Loss(xk, df=loss_args[0], coupons_cf=loss_args[1], streak_data=loss_args[2], weight_scheme=loss_args[4]) print(xk, loss) while (Z(np.linspace(0.001, 30, 1000), beta_init)).min() < 0 or i == 0.1: res = minimize(fun=Loss, x0=beta_init, args=loss_args, bounds=bounds, callback=callback_print, **kwargs) beta_init = res.x bounds = ((0, 1), (None, None), (None, None), (beta_init[-1] + i, 0.05 * max_deal_span)) i += 0.1 return res
def is_outlier(self, points, thresh=3.5, score_type='mzscore'): ''' Returns a boolean array with True if points are outliers and False otherwise. Parameters: ----------- points : An numobservations by numdimensions array of observations thresh : The modified z-score to use as a threshold. Observations with a modified z-score (based on the median absolute deviation) greater than this value will be classified as outliers. Returns: -------- mask : A numobservations-length boolean array. References: ---------- Boris Iglewicz and David Hoaglin (1993), 'Volume 16: How to Detect and Handle Outliers', The ASQC Basic References in Quality Control: Statistical Techniques, Edward F. Mykytka, Ph.D., Editor. ''' if score_type == 'zscore': thresh = 2 # if len(points.shape) >= 1: # points = points.loc[:,'ytm'][:,None] if (self.inertia) & (len(self.previous_curve) != 0): self.logger.debug( f'diff to previous curve, Z-score threshold: {thresh}') diff = ( points.loc[:, 'ytm'] * 100 - (np.exp(Z(points.loc[:, 'span'] / 365, self.previous_curve)) - 1) * 100) else: # self.logger.debug(points) self.logger.debug('first filtering') mean = np.mean(points.loc[:, 'ytm']) diff = (points.loc[:, 'ytm'] - mean) sstd = np.std(diff) z_score = np.abs(diff / sstd) elif score_type == 'mzscore': # if len(points.shape) >= 1: # points = points.loc[:,'ytm'][:,None] if (self.inertia) & (len(self.previous_curve) != 0): self.logger.debug( f'diff to previous curve, modified Z-score threshold: {thresh}' ) diff = np.abs(points.loc[:, 'ytm'] - (np.exp( par_yield(points.loc[:, 'span'].values / 365, self.previous_curve)) - 1)) * 100 else: self.logger.debug('first filtering') median = np.median(points.loc[:, 'ytm']) # diff = (points.loc[:,'ytm']*100- median*100)**2 diff = np.abs(points.loc[:, 'ytm'] - median) * 100 # sstd = np.sqrt(diff) sstd = np.median(diff) #med_abs_deviation z_score = 0.6745 * diff / sstd return (z_score, (z_score > thresh), sstd)