def construct_discountbond(bond): value_date = bond.get('value_date') issue_date = bond.get('issue_date') use_date = None if value_date is not None and issue_date is not None: if issue_date >= value_date: use_date = issue_date else: use_date = value_date elif value_date is not None: use_date = value_date elif issue_date is not None: use_date = issue_date dcfs = [] times = [] cfs = [] cpns = [] dates2 = [] if use_date is not None: dcf = day_cf('Actual/365', use_date, bond['maturity'], bondmat_date=bond['maturity']) dcfs.append(dcf) times.append(dcf) cfs.append(bond['position']) dates2.append(bond['maturity']) # cash_flows = {} cash_flows = {'dates': dates2, 'dcfs': dcfs, 'cfs': cfs, 'times': times} bond['cash_flows'] = cash_flows bond['coupon_structure'] = cpns
dfs = dcurve.discount_factor_gen(rates, return_type="time") # print(dfs) # %% # Define VaR Vertices using tenors # convert the tenors to date then to no of days from faspy.interestrate.rmp_dates import tenor_to_maturity as ttm, \ day_count_factor as day_cf var_vertices = ["6M", "1Y", "3Y", "5Y", "10Y", "20Y"] var_dates = list( map(lambda tenor: ttm(vdate, tenor, business_day="Modified Following"), var_vertices)) var_days = list(map(lambda date: (date - vdate).astype("int"), var_dates)) var_time = list(map(lambda date: day_cf("Actual/365", vdate, date), var_dates)) # %% # get historical discount factors hdf = [] for df in dfs: x_axis = [x["times"] for x in df] y_axis = [y["df"] for y in df] idf = dcurve.interpolation(x_axis, y_axis, var_time) hdf.append(idf) #print(hdf) # %% # 1.Calculate the log normal return # 2. Calculate volatility # 3. Calculate Correlation
def construct_fixbond(bond): bus_day = None if bond['business_day'] == 'NULL': bus_day = 'No Adjustment' else: bus_day = bond['business_day'] if bond['day_count'] == 'NULL': day_count = 'Actual/Actual ICMA' else: day_count = bond['day_count'] start_date = bond.get('issue_date') if start_date is not None: start_date = dt64(start_date, 'D') value_date = bond.get('value_date') if value_date is not None: value_date = dt64(value_date, 'D') if start_date is not None and value_date is not None: use_date = start_date elif start_date is not None: use_date = start_date elif value_date is not None: use_date = value_date else: raise Exception('Both value_date and issue_date do not have any value') dates = gen_dates(use_date, bond['maturity'], issueDate=start_date, frequency=fre[bond['frequency']], business_day=bus_day, method=bond['date_generation']) #print(dates) # ********************************************************************************* # # CALCULATING THE DATA FOR FULL STRUCTURES # # ********************************************************************************* fs = list(dates) flen = len(fs) for i in range(flen): fs_sgl = fs[i] fs_sgl['fv_flow'] = 0.00 fs_sgl['fv'] = bond['face_value'] if i > 0: fs_prev = fs[i - 1] fs_sgl['cpn_dcf'] = day_cf(day_count, fs_prev['date'], fs_sgl['date'], bondmat_date=fs[-1]['date'], next_coupon_date=fs_sgl['date'], Frequency=12 / fre[bond['frequency']]) fs_sgl['coupon'] = bond['coupon'] fs_sgl['time'] = day_cf('Actual/365', use_date, fs_sgl['date'], bondmat_date=fs[-1]['date'], next_coupon_date=fs_sgl['date'], Frequency=12 / fre[bond['frequency']]) fs_sgl['cf'] = (fs_sgl['coupon'] * fs_sgl['cpn_dcf'] * 0.01 * fs_sgl['fv']) else: fs_sgl['cpn_dcf'] = 0.00 fs_sgl['coupon'] = 0.00 fs_sgl['fv'] = 0.00 fs_sgl['cf'] = 0.00 fs[-1]['fv_flow'] = bond['face_value'] bond['full_structures'] = fs # ********************************************************************************* # # CALCULATING THE DATA FOR ACTIVE STRUCTURES # # ********************************************************************************* if value_date is None: bond['active_structures'] = {} elif value_date is not None and start_date is None: bond['active_structures'] = bond['full_structures'] elif value_date < start_date: bond['active_structures'] = bond['full_structures'] else: booldate = list(map(lambda x: x['date'] > value_date, fs)) index = booldate.index(True) # exclude paid coupon period/start_date acs = list(fs[index - 1:]) acs[0]['dcf'] = 0 acs[1]['dcf'] = day_cf(day_count, bond['value_date'], acs[1]['date'], bondmat_date=acs[-1]['date'], next_coupon_date=acs[1]['date'], Frequency=12 / fre[bond['frequency']]) alen = len(acs) for i in range(alen): acs[i]['time'] = day_cf('Actual/365', bond['value_date'], acs[i]['date'], bondmat_date=acs[-1]['date'], next_coupon_date=acs[i]['date']) bond['active_structures'] = acs
def frn_price(bond, disc_curve): try: val_date = dt64(bond.get('value_date')) except Exception: raise Exception('Value date is not a date') try: acs = bond['active_structures'] except Exception: raise Exception('full_structures does not have the required key(s)') bond['last_coupon_date'] = acs[0]['date'] bond['accrued_interest'] = 0 next_cpn = acs[1] interp = disc_curve.interpolate(1, is_function=True) # Calculation for fixed coupon if bond['value_date'] >= acs[0]['date']: coupon = next_cpn['coupon'] if coupon is not None: gen_time = next_cpn['time'] next_cpn['cf'] = coupon * next_cpn['dcf'] * next_cpn['fv'] * 0.01 dcf = next_cpn['dcf'] df = interp(gen_time) next_cpn['ref_rate'] = (1 / df - 1) / (dcf * 0.01) next_cpn['ref_with_spread'] = (next_cpn['ref_rate'] + bond['spread']) df = 1 / (1 + next_cpn['ref_with_spread'] * dcf * 0.01) next_cpn['pv'] = (next_cpn['cf'] + next_cpn['fv_flow']) * df next_cpn['df'] = df # Calculation for PVBP01 next_cpn['coupon01'] = coupon next_cpn['cf01'] = next_cpn['cf'] df = interp(gen_time) next_cpn['ref_rate01'] = (1 / df - 1) / (dcf * 0.01) + 0.01 next_cpn['ref01_with_spread'] = (next_cpn['ref_rate01'] + bond['spread']) #print(next_cpn['ref_rate01'], bond['spread']) df01 = 1 / (1 + next_cpn['ref01_with_spread'] * dcf * 0.01) next_cpn['pv01'] = (next_cpn['cf01'] + next_cpn['fv_flow']) * df01 next_cpn['df01'] = df01 accrued_dcf = day_cf(bond['day_count'], bond['last_coupon_date'], bond['value_date'], bondmat_date=acs[-1]['date'], next_coupon_date=next_cpn['date'], Frequency=12 / fre[bond['frequency']]) #print(bond.get('current_coupon'), next_cpn) bond['accrued'] = accrued_dcf bond['accrued_interest'] = (accrued_dcf * next_cpn['coupon'] * 0.01 * next_cpn['fv']) lacs = len(acs) # Calculation for unfixed coupon for i in range(2, lacs, 1): prev_acs = acs[i - 1] sgl_acs = acs[i] #sgl_acs['fixed'] = False time0 = prev_acs['time'] time1 = sgl_acs['time'] df0 = disc_curve.interpolate(time0) df1 = disc_curve.interpolate(time1) dcf = sgl_acs['dcf'] fwd_df = df1 / df0 sgl_acs['ref_rate'] = (1 / fwd_df - 1) / (dcf * 0.01) sgl_acs['coupon'] = (sgl_acs['ref_rate'] + sgl_acs['margin']) sgl_acs['cf'] = sgl_acs['coupon'] * dcf * 0.01 * sgl_acs['fv'] sgl_acs['ref_with_spread'] = (sgl_acs['ref_rate'] + bond['spread']) sgl_acs['fwd_df'] = 1 / (1 + sgl_acs['ref_with_spread'] * dcf * 0.01) sgl_acs['df'] = sgl_acs['fwd_df'] * prev_acs['df'] sgl_acs['pv'] = (sgl_acs['cf'] + sgl_acs['fv_flow']) * sgl_acs['df'] # Calculation for PVBP01 sgl_acs['ref_rate01'] = (1 / fwd_df - 1) / (dcf * 0.01) + 0.01 sgl_acs['coupon01'] = (sgl_acs['ref_rate01'] + sgl_acs['margin']) sgl_acs['cf01'] = sgl_acs['coupon01'] * dcf * 0.01 * sgl_acs['fv'] sgl_acs['ref01_with_spread'] = (sgl_acs['ref_rate01'] + bond['spread']) sgl_acs['fwd_df01'] = 1 / (1 + sgl_acs['ref01_with_spread'] * dcf * 0.01) sgl_acs['df01'] = sgl_acs['fwd_df01'] * prev_acs['df01'] sgl_acs['pv01'] = ((sgl_acs['cf01'] + sgl_acs['fv_flow']) * sgl_acs['df01']) sum_pv = 0 sum_pv01 = 0 for i in range(1, lacs, 1): sgl_acs = acs[i] sum_pv += sgl_acs['pv'] sum_pv01 += sgl_acs['pv01'] bond['pvbp01'] = sum_pv01 - sum_pv bond['proceed'] = sum_pv bond['price per 100'] = ((bond['proceed'] - bond['accrued_interest']) / bond['face_value'] * 100)
def fixbond_price(value_date, bond, yld): try: val_date = dt64(value_date) except Exception: raise Exception('Value date is not a date') try: acs = bond['active_structures'] except Exception: raise Exception('full_structures does not have the required key(s)') try: freq = bond['frequency'] except Exception: raise Exception('Error getting frequency of the floating leg') if bond['value_date'] is None: raise Exception('Value date for the bond is not set') bond['last_coupon_date'] = acs[0]['date'] bond['accrued_interest'] = 0 next_cpn = acs[1] # Calculation for fixed coupon if bond['value_date'] >= acs[0]['date']: coupon = bond.get('coupon') if coupon is not None: #print(next_cpn) next_cpn['coupon'] = coupon next_cpn['cf'] = coupon * next_cpn['dcf'] * next_cpn['fv'] * 0.01 y_dcf = day_cf(bond['day_count'], val_date, next_cpn['date'], bondmat_date=acs[-1]['date'], next_coupon_date=next_cpn['date'], Frequency=12 / fre[bond['frequency']]) next_cpn['y_dcf'] = y_dcf df = 1 / (1 + yld * y_dcf * 0.01) next_cpn['pv'] = (next_cpn['cf'] + next_cpn['fv_flow']) * df next_cpn['df'] = df next_cpn['p_df'] = df # Calculation for PVBP01 df01 = 1 / (1 + (yld + 0.01) * y_dcf * 0.01) next_cpn['pv01'] = (next_cpn['cf'] + next_cpn['fv_flow']) * df01 next_cpn['df01'] = df01 next_cpn['p_df01'] = df01 #print(next_cpn) accrued_dcf = day_cf(bond['day_count'], bond['last_coupon_date'], bond['value_date'], bondmat_date=acs[-1]['date'], next_coupon_date=next_cpn['date'], Frequency=12 / fre[bond['frequency']]) # print(bond.get('current_coupon'), next_cpn) bond['accrued'] = accrued_dcf bond['accrued_interest'] = (accrued_dcf * next_cpn['coupon'] * 0.01 * next_cpn['fv']) lacs = len(acs) for i in range(2, lacs, 1): acs_sgl = acs[i] lastrow = acs[i - 1] acs_sgl['y_dcf'] = acs_sgl['cpn_dcf'] acs_sgl['p_df'] = 1 / (1 + yld * acs_sgl['y_dcf'] * 0.01) acs_sgl['p_df01'] = 1 / (1 + (yld + 0.01) * acs_sgl['y_dcf'] * 0.01) acs_sgl['df'] = acs_sgl['p_df'] * lastrow['df'] acs_sgl['df01'] = acs_sgl['p_df01'] * lastrow['df01'] acs_sgl['pv'] = ((acs_sgl['cf'] + acs_sgl['fv_flow']) * acs_sgl['df']) acs_sgl['pv01'] = ((acs_sgl['cf'] + acs_sgl['fv_flow']) * acs_sgl['df01']) sum_pv = 0 sum_pv01 = 0 for i in range(1, lacs, 1): sgl_acs = acs[i] sum_pv += sgl_acs['pv'] sum_pv01 += sgl_acs['pv01'] bond['pvbp01'] = sum_pv01 - sum_pv bond['proceed'] = sum_pv bond['price per 100'] = ((bond['proceed'] - bond['accrued_interest']) / bond['face_value'] * 100) macD = 0 for i in range(1, lacs, 1): acs_sgl = acs[i] macD = (macD + acs_sgl['time'] * (acs_sgl['cf'] + acs_sgl['fv_flow']) * acs_sgl['df']) macD = macD / bond['proceed'] bond['modified duration'] = -macD / (1 + (yld * 0.01) / (12 / fre[freq])) bond['macaulay duration'] = macD #print('acs===>', acs) derivatives = bond_risk(acs, yld) bond['duration'] = derivatives['duration'] bond['convexity'] = derivatives['convexity'] bond['active_structures'] = acs else: # ******************************************************************* # else clause process the bond as a forward issuance bond # disfac (discount factor) has to be provided to properly price the # bond) yld is taken to be the fwd yield of the bond # ******************************************************************** raise Exception('Forward bond issuance is yet to be done')
def construct_frn(bond, holidays=[]): bdc = np.busdaycalendar(weekmask='1111100', holidays=holidays) bus_day = None if bond['business_day'] == 'NULL': bus_day = 'No Adjustment' else: bus_day = bond['business_day'] if bond['day_count'] == 'NULL': day_count = 'Actual/365' else: day_count = bond['day_count'] start_date = bond.get('issue_date') if start_date is not None: start_date = dt64(start_date, 'D') value_date = bond.get('value_date') if value_date is not None: value_date = dt64(value_date, 'D') if start_date is not None and value_date is not None: use_date = start_date elif start_date is not None: use_date = start_date elif value_date is not None: use_date = value_date else: raise Exception('Both value_date and issue_date do not have any value') dates = gen_dates(use_date, bond['maturity'], issueDate=start_date, frequency=fre[bond['frequency']], business_day=bus_day, method=bond['date_generation']) #print(dates) # ********************************************************************************* # # CALCULATING THE DATA FOR FULL STRUCTURES # # ********************************************************************************* fs = list(dates) flen = len(fs) for i in range(flen): row = fs[i] row['cf'] = 0 row['time'] = day_cf('Actual/365', use_date, row['date'], bondmat_date=fs[-1]['date'], next_coupon_date=row['date']) if i == 0: row['dcf'] = 0 row['y_dcf'] = 0 row['margin'] = 0 row['fv'] = 0 row['fv_flow'] = 0 row['fixing_date'] = None else: row['dcf'] = day_cf(day_count, fs[i - 1]['date'], row['date'], bondmat_date=fs[-1]['date'], next_coupon_date=row['date'], Frequency=12 / fre[bond['frequency']]) row['y_dcf'] = row['dcf'] row['margin'] = bond['margin'] row['fv'] = bond['face_value'] row['fv_flow'] = 0 if i == flen - 1: row['fv_flow'] = bond['face_value'] row['fixing_date'] = bus_off(fs[i - 1]['date'], -start_basis[bond['fixing_basis']], roll='backward', busdaycal=bdc) bond['full_structures'] = fs # ********************************************************************************* # # CALCULATING THE DATA FOR ACTIVE STRUCTURES # # ********************************************************************************* if value_date is None: bond['active_structures'] = {} elif value_date is not None and start_date is None: bond['active_structures'] = bond['full_structures'] elif value_date < start_date: bond['active_structures'] = bond['full_structures'] else: # print(value_date,start_date) booldate = list(map(lambda x: x['date'] > value_date, fs)) index = booldate.index(True) # exclude paid coupon period/start_date acs = list(fs[index - 1:]) acs[0]['y_dcf'] = 0 acs[1]['y_dcf'] = day_cf(day_count, bond['value_date'], acs[1]['date'], bondmat_date=acs[-1]['date'], next_coupon_date=acs[1]['date'], Frequency=12 / fre[bond['frequency']]) alen = len(acs) for i in range(alen): acs[i]['time'] = day_cf('Actual/365', bond['value_date'], acs[i]['date'], bondmat_date=acs[-1]['date'], next_coupon_date=acs[i]['date']) bond['active_structures'] = acs return bond