def price_Loss(beta, df, coupons_cf, streak_data, rho=0.2, weight_scheme='rev_span', 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]) ind = df.index Price = (D(streak_data[ind], beta) * coupons_cf[ind]).sum().values Loss = np.linalg.norm(weight(beta, df=df, rho=rho, weight_scheme=weight_scheme) * (df.stand_price.values - Price)) return Loss
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 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]) ind = df.index #estimating price Price = (D(streak_data[ind], beta) * coupons_cf[ind]).sum().values if (Price <= 0).any(): Loss = 1e100 else: ytm_hat = np.array([ newton_estimation(df.iloc[i], Price[i], coupons_cf, streak_data, maxiter=200) for i in range(df.shape[0]) ]) #calculatting Loss W = weight(beta, df=df, rho=rho, weight_scheme=weight_scheme) Loss = (W * np.square(df['ytm'].values - ytm_hat)).sum() return Loss
#Parameters bounds for constraint optimization bounds = ((0, 1), (None, None), (None, None), (CONFIG.TETA_MIN, CONFIG.TETA_MAX)) #Maturity limit for Zero-curve plot longest_maturity_year = max([max_deal_span, 20]) theor_maturities = np.linspace(0.001, longest_maturity_year, 10000) options = {'maxiter': 500, 'eps': 9e-5, 'disp': True} #Tuple of arguments for loss function loss_args = (filtered_data, coupons_cf, streak_data, CONFIG.RHO, CONFIG.WEIGHT_SCHEME) #defining loss loss = yield_Loss filtered_data['weight'] = weight([1, 1, 1, 1], filtered_data, CONFIG.WEIGHT_SCHEME) ###### OPTIMIZATION print('start optimization\n') res_ = iter_minimizer(Loss=loss, beta_init=x0, loss_args=loss_args, method='SLSQP', bounds=bounds, constraints=constr, max_deal_span=max_deal_span, options=options) beta_best = res_.x print('end optimization\n') ### Showing results of work draw(beta_best, filtered_data, theor_maturities, f'{CONFIG.SETTLE_DATE:%d.%m.%Y}', longest_maturity_year, draw_points=True,
def draw(beta, df, theor_maturities, title_date, longest_maturity_year, weight_scheme=None, ax=None, draw_points=True, label=None, shift=False, **kwargs): ''' Drawing spot curve based on beta Parameters ------------ beta: list-like Should be length of 4. Nelson-Siegel's vector of parameters df: Pandas Dataframe Dataframe of bonds' data theor_maturities: list-like Vector of maturities title_date: str or datetime Settle date of the curve's construction longest_maturity_year: int Maximum tenor on which plot will be drawn weight_scheme: 'str', default: None Weights of deals ax: Matplotlib Axes, default: None draw_points: bool Whether to draw deals' ytm on plot or not label: str, default: None shift: bool, default: False If shift true transforms spot rate curve from discrete to continuous ''' #defining zero curbe beta = beta.copy() spot_rates = spot_nelson(theor_maturities, beta) if shift: spot_rates = (np.exp(spot_rates) - 1) #setting axes if ax is None: fig, ax = plt.subplots(figsize=(15, 10)) if label is None: beta[:3] *= 100 label = f'{beta.round(2)}' ax.plot(theor_maturities, spot_rates, label=label, **kwargs) #draw scatterplot of deals or not? if draw_points: y_scatter = df['ytm'].values x_scatter = df['span'].values / 365 #size of points; depends on weight of transaction s = (weight(beta, df, weight_scheme=weight_scheme) / weight(beta, df, weight_scheme=weight_scheme).sum()) ax.scatter(x_scatter, y_scatter, s=s * 15 * 1e3, facecolors='none', edgecolors='grey', alpha=0.9) #setting labels, ticks, legend and title ax.set_title(f'Curves at {title_date} for {df.shape[0]} deals') ax.set_ylabel('%') ax.set_xlabel('Tenor in years') ax.set_xticks(np.arange(0, longest_maturity_year + 1, 1)) ax.legend()