def _dates_gen_structure(bond): """ Generate date structures. Function can be used for all coupon bearing products with bullet principal repayment Parameters: bonds: a dictionary with the following keys - value_date, business_day, issue_date, value_date, maturity, frequency, day_count, date_generation. Returns: a dictionary or an array of dictionaries with the following keys - "start_date" and "end_date" """ bus_day = None if bond['business_day'] == 'NULL': bus_day = 'No Adjustment' else: bus_day = bond['business_day'] 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 is a deque dates = gen_dates(use_date, bond['maturity'], issueDate=start_date, frequency=fre[bond['frequency']], business_day=bus_day, method=bond['date_generation']) start_dates = deque(dates) end_dates = deque(dates) start_dates.pop() end_dates.popleft() noofcpns = len(start_dates) structures = deque() for no in range(noofcpns): structure = {"start_date": start_dates[no], "end_date": end_dates[no]} structures.append(structure) newstructure = list( map(lambda sdate, edate: { "start_date": sdate, "end_date": edate }, start_dates, end_dates)) return newstructure
def discount_factor_from_ytm_using_structures(value_date, date_structure, day_count, frequency, business_day, ytm): df = 1 maturity = date_structure[-1]["end_date"] data = [{ "start_date": x["start_date"], "end_date": x["end_date"] } for x in date_structure if value_date < dt64(x["end_date"])] for datum in data: if dt64(datum["start_date"]) >= value_date: datum["dcf"] = day_cf(day_count, datum["start_date"], datum["end_date"], bondmat_date=maturity, next_coupon_date=datum["end_date"], business_day=business_day, Frequency=12 / frequencies[frequency]) datum["time"] = day_cf("Actual/365", value_date, datum["end_date"], bondmat_date=maturity, next_coupon_date=datum["end_date"], business_day=business_day, Frequency=12 / frequencies[frequency]) elif datum["start_date"] < value_date: datum["dcf"] = day_cf(day_count, value_date, datum["end_date"], bondmat_date=maturity, next_coupon_date=datum["end_date"], business_day=business_day, Frequency=12 / frequencies[frequency]) datum["time"] = day_cf("Actual/365", value_date, datum["end_date"], bondmat_date=maturity, next_coupon_date=datum["end_date"], business_day=business_day, Frequency=12 / frequencies[frequency]) datum["period_df"] = 1 / (1 + ytm * datum["dcf"] / 100) df = datum["df"] = df * datum["period_df"] disfac = list( map(lambda datum: { "times": datum["time"], "df": datum["df"] }, data)) disfac.insert(0, {"times": 0, "df": 1}) return disfac
def calc_df_from_shortrate(startdate, enddate, rate, day_count, rate_basis="Money Market"): sdate = dt64(startdate, "D") edate = dt64(enddate, "D") dcf = day_cf(day_count, sdate, edate, next_coupon_date=edate) if rate_basis == "Discount Rate": return _dr2df(rate, dcf) else: return _mmr2df(rate, dcf) return None
def movedatebymonth(your_date, no_of_month=1, business_day='No Adjustment', holidays=[]): bdc = np.busdaycalendar(weekmask='1111100', holidays=holidays) start_m = dt64(your_date, 'M') day = dt64(your_date, 'D') - dt64(start_m, 'D') end_m = start_m + no_of_month next_m = end_m + 1 days_in_end_m = dt64(next_m, 'D') - dt64(end_m, 'D') if days_in_end_m > day: enddate = dt64(end_m, 'D') + day else: enddate = dt64(end_m, 'D') + days_in_end_m - 1 if business_day == 'No Adjustment' or business_day is None: return enddate elif business_day == 'Following': return np.busday_offset(enddate, 0, roll='forward', busdaycal=bdc) elif business_day == 'Preceeding': return np.busday_offset(enddate, 0, roll='backward', busdaycal=bdc) elif business_day == 'Modified Following': new_date = np.busday_offset(enddate, 0, roll='forward', busdaycal=bdc) if dt64(new_date, 'M') > end_m: return np.busday_offset(enddate, 0, roll='backward', busdaycal=bdc) else: return new_date
def flat_curve(start_date, end_date, rate, rate_basis="Money Market", day_count="Actual/365", bus_day="No Adjustment", tenors=None, return_type="time"): if tenors is None: mytenors = [x for x in std_tenors if x != "12M"] else: mytenors = list(tenors) sdate = dt64(start_date, "D") edate = dt64(end_date, "D") time = day_cf("Actual/365", sdate, edate) dcf = day_cf(day_count, sdate, edate) if rate_basis == "Money Market": df = _mmr2df(float(rate), float(dcf)) elif rate_basis == "Discount Rate": df = _dr2df(float(rate), float(dcf)) else: return None time = day_cf("Actual/365", sdate, edate) crate = -math.log(df) / time dates = [ttm(sdate, tenor, day_count, bus_day) for tenor in mytenors] times = [{ "times": float(day_cf("Actual/365", sdate, date)) } for date in dates] timelen = len(times) ret_array = [] if return_type == "time": dfs = [{"df": math.exp(-crate * time["times"])} for time in times] for i in range(timelen): time = times[i] df = dfs[i] time.update(df) ret_array.append(time) elif return_type == "days": days = [{"days": (date - sdate).astype("float")} for date in dates] dfs = [{"df": math.exp(-crate * time["times"])} for time in times] for i in range(timelen): day = days[i] df = dfs[i] day.update(df) ret_array.append(day) return ret_array
def _dcf_a365_4ayear(start, end, business_day=None, Frequency=1): year1 = np.datetime64(start, 'Y') year2 = np.datetime64(dt64(end), 'Y') if year2 == year1: daysinayear = daysintheyear(start) days = np.int64(dt64(end) - start) return days/daysinayear else: daysinayear1 = daysintheyear(start) end1 = np.datetime64(year1 + 1, 'D') days1 = np.int64(end1 - start) daysinayear2 = daysintheyear(end) start2 = np.datetime64(year2, 'D') days2 = np.int64(dt64(end) - start2) return days1/daysinayear1 + days2/daysinayear2
def current_date(self, current_date): if current_date is not None: try: self.__current_date = dt64(current_date) except: raise ValueError('current date is not a date') else: self.__current_date = current_date
def value_date(self, value_date): if value_date is not None: try: self.__value_date = dt64(value_date, "D") except: raise ValueError('value_date is not a date') else: self.__value_date = value_date
def start_date(self, start_date): if start_date is not None: try: self.__start_date = dt64(start_date) except: raise ValueError('start_date is not a date') else: self.__start_date = start_date
def calc_shortrate_from_df(startdate, enddate, df, day_count, rate_basis="Money Market"): sdate = dt64(startdate, "D") edate = dt64(enddate, "D") dcf = day_cf(day_count, sdate, edate, bondmat_date=edate, next_coupon_date=edate) if rate_basis == "Discount Rate": return _df2dr(df, dcf) else: return _df2mmr(float(df), float(dcf)) return None
def adjustdates(months, dates, business_day, holidays=[]): bdc = np.busdaycalendar(weekmask='1111100', holidays=holidays) datalen = len(dates) newdates = [] for i in range(datalen): month = months[i] dmonth = dt64(dates[i], 'M') if dmonth == month: if business_day == 'Following': dates[i] = np.busday_offset(dates[i], 0, roll='forward', busdaycal=bdc) elif business_day == 'Preceeding': dates[i] = np.busday_offset(dates[i], 0, roll='backward', busdaycal=bdc) elif business_day == 'Modified Following': new_date = np.busday_offset(dates[i], 0, roll='forward', busdaycal=bdc) if dt64(new_date, 'M') > month: dates[i] = np.busday_offset(dates[i], 0, roll='backward', busdaycal=bdc) elif dmonth > month: #print(month,dmonth) nextmonth = month + td64(1, 'M') nextmonthdate = dt64(nextmonth, 'D') days = _datediff(dt64(month, 'D'), nextmonthdate) dates[i] = dt64(month, 'D') + days - 1 if business_day == 'Following': dates[i] = np.busday_offset(dates[i], 0, roll='forward', busdaycal=bdc) elif business_day == 'Preceeding': dates[i] = np.busday_offset(dates[i], 0, roll='backward', busdaycal=bdc) elif business_day == 'Modified Following': new_date = np.busday_offset(dates[i], 0, roll='forward', busdaycal=bdc) if dt64(new_date, 'M') > month: dates[i] = np.busday_offset(dates[i], 0, roll='backward', busdaycal=bdc)
def cut_log_spectra(fileinpaths, times, fileoutpaths_list, **kwargs): for i, fileinpath in enumerate(fileinpaths): fileoutpaths = fileoutpaths_list[i] tdmsfile = TF(fileinpath) for j, t in enumerate(times): fileoutpath = fileoutpaths[j] direc = os.path.split(fileoutpath)[0] if not os.path.exists(direc): os.makedirs(direc) root_object = RootObject(properties={}) try: with TdmsWriter(fileoutpath, mode='w') as tdms_writer: timedata = [ dt64(y) for y in tdmsfile.channel_data('Global', 'Time') ] idx1, idx2 = _get_indextime(timedata, t[0], t[1]) if idx1 == idx2: pass else: for group in tdmsfile.groups(): group_object = GroupObject(group, properties={}) if group == "Global": for channel in tdmsfile.group_channels(group): if channel.channel == 'Wavelength': channel_object = ChannelObject( channel.group, channel.channel, channel.data) else: channel_object = ChannelObject( channel.group, channel.channel, channel.data[idx1:idx2]) tdms_writer.write_segment([ root_object, group_object, channel_object ]) else: for channel_object in tdmsfile.group_channels( group)[idx1:idx2]: tdms_writer.write_segment([ root_object, group_object, channel_object ]) except ValueError as error: print(error) print('removing the file at: \n', fileoutpath) os.remove(fileoutpath)
def _dcf_a365(prev_date, current_date, business_day=None, Frequency=1): year1 = np.datetime64(prev_date, 'Y') year2 = np.datetime64(current_date, 'Y') years = np.int64(year2 - year1) if years == 0: return _dcf_a365_4ayear(prev_date, current_date, business_day=business_day, Frequency=Frequency) else: start_m = np.datetime64(prev_date, 'M') start_day = np.int64( np.datetime64(prev_date, 'D') - np.datetime64(start_m, 'D')) years_enddate_m = np.datetime64(prev_date, 'M') + years * 12 years_enddate = np.datetime64(years_enddate_m, 'D') + start_day extra_days = np.int64(dt64(current_date) - years_enddate) if years == 1: if extra_days == 0: return years elif extra_days < 0: return _dcf_a365_4ayear(prev_date, current_date, business_day=business_day, Frequency=Frequency) else: yearfrac = _dcf_a365_4ayear(years_enddate, current_date, business_day=business_day, Frequency=Frequency) return years + yearfrac else: if extra_days == 0: return years else: yearfrac = _dcf_a365_4ayear(years_enddate, current_date, business_day=business_day, Frequency=Frequency) return years + yearfrac
def fixbond_value(value_date, structures, yld, day_count, frequency, business_day="No Adjustment"): const = 0.01 try: ytm = float(yld) ytm1 = ytm + const ytm2 = ytm1 + const except Exception: return None newstructures = [dict(x) for x in structures] maturity = newstructures[-1]["end_date"] # Curves to be used in calculation of duration, convexity and pvbp01 df_curve = ytm_df_struct(value_date, newstructures, day_count, frequency, business_day, ytm) x_axis = [x["times"] for x in df_curve] y_axis = [x["df"] for x in df_curve] ifunc = interpolation(x_axis, y_axis, float(1 / 366), is_function=True) df_curve1 = ytm_df_struct(value_date, newstructures, day_count, frequency, business_day, ytm1) x_axis1 = [x["times"] for x in df_curve1] y_axis1 = [x["df"] for x in df_curve1] ifunc1 = interpolation(x_axis1, y_axis1, float(1 / 366), is_function=True) df_curve2 = ytm_df_struct(value_date, newstructures, day_count, frequency, business_day, ytm2) x_axis2 = [x["times"] for x in df_curve2] y_axis2 = [x["df"] for x in df_curve2] ifunc2 = interpolation(x_axis2, y_axis2, float(1 / 366), is_function=True) # covert data into list dates = [{ "start_date": x["start_date"], "end_date": x["end_date"] } for x in structures if value_date < dt64(x["end_date"])] cfs = np.asarray([ x["cash_flow"] for x in structures if value_date < dt64(x["end_date"]) ]) times = np.asarray( [day_cf("Actual/365", value_date, x["end_date"]) for x in dates]) # interpolating discount factors dfs = [ifunc(x) for x in times] # calculating the present values pvs = cfs * dfs weighted_pvs = pvs * times value = np.sum(pvs) weighted_value = np.sum(weighted_pvs) # interpolating 2nd discount factors dfs1 = [ifunc1(x) for x in times] # calculating the 2nd present values pvs1 = cfs * dfs1 value1 = np.sum(pvs1) # interpolating 3rd discount factors dfs2 = [ifunc2(x) for x in times] # calculating the 3rd present values pvs2 = cfs * dfs2 value2 = np.sum(pvs2) mac_dur = weighted_value / value compound = 12 / frequencies[frequency] mod_dur = mac_dur / pow((1 + ytm / (compound * 100)), compound) pvbp01 = value1 - value # 1st derivative at ytm using forward differential der0 = (value1 - value) / (ytm1 - ytm) # 1st derivative at ytm1 using forward differential der1 = (value2 - value1) / (ytm2 - ytm1) # 2nd derivative at ytm using forward differential conv = (der1 - der0) / (ytm2 - ytm1) return { "macaulay_duration": mac_dur, "modified_duration": mod_dur, "pvbp01": pvbp01, "convexity": conv, "value": value }
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Wed Nov 4 07:32:58 2020 @author: RMS671214 """ from faspy.interestrate import discount_curve as dcurve from numpy import datetime64 as dt64 import time vdate = dt64("2020-10-30", "D") start0 = time.perf_counter() # %% # Historical Discount Factors rates = [] rate = { "value_date": "2020-10-30", "st_busday": "Modified Following", "st_ratebasis": "Simple", "st_daycount": "Actual/365", "lt_busday": "No Adjustment", "lt_frequency": "Semi-Annual", "lt_daycount": "Actual/Actual", "rates": { 'O/N': 2.30, '1W': 2.35, '1M': 2.45, '3M': 2.55, '6M': 2.65, '1Y': 2.70,
def bucketing(data, buckets, value_date, dfcurve=None): """ Generate bonds coupon structures. Parameters: data: a list containing dictionary with the following keys - "id", "date" and "amount" buckets: a list containg dictionaries with the following keys - "name", "from" and "to". The value for each key is number of days Returns: a dictionary with the following keys - date, dcf, time, days, df, rate """ vdate = dt64(value_date, "D") mydata = list(data) calc_data = [{ "days": _datediff(vdate, dt64(x["date"], "D")).astype("int"), "tenor": day_cf("Actual/365", vdate, dt64(x["date"], "D")) } for x in mydata] if dfcurve is not None: df_xaxis = [x["times"] for x in dfcurve] df_yaxis = [x["df"] for x in dfcurve] i_func = interpolation(df_xaxis, df_yaxis, 1, is_function=True) df_array = [{"df": float(i_func(x["tenor"]))} for x in calc_data] dtlen = len(mydata) newdata = [] for i in range(dtlen): old = dict(mydata[i]) new = dict(calc_data[i]) old.update(new) if dfcurve is not None: df_dic = df_array[i] old.update(df_dic) old["pv"] = old["df"] * old["amount"] newdata.append(old) mdur = [{ "mod_duration": _mod_duration(float(x["tenor"]), float(x["df"])) } for x in newdata] bucket = [{"bucket": assign_bucket(x["tenor"], buckets)} for x in newdata] for i in range(dtlen): old = newdata[i] new = mdur[i] buc = bucket[i] old.update(new) old.update(buc) sortedbucket = sorted(buckets, key=sorter) buclen = len(sortedbucket) bucket_list = {} # return sortedbucket for i in range(buclen): bucket_list[sortedbucket[i]["name"]] = 0 for datum in newdata: bucket_list[datum["bucket"]] += datum["pv"] return (newdata, bucket_list)
def _calc_shorttenor(tenors, rates, setting, holidays=[]): v_date = dt64(setting['cdate']) tenors1 = tenors[3:] tenors2 = tenors[:3] tomdate, spotdate, spotnext = _calc_ondate(v_date, holidays=holidays) dates = {'current': v_date, 'Tom': tomdate, 'Spot': spotdate} data_len = len(tenors) data = [] rates1 = rates[3:] rates2 = rates[:3] ratebasis = 0 if setting['rate_basis'] == 'Discount Rate': ratebasis = 1 data_len = len(tenors2) for i in range(data_len): datum = {} datum['tenor'] = tenors2[i] datum['rate'] = rates2[i] if datum['tenor'] == 'O/N': datum['start'] = v_date datum['maturity'] = tomdate elif datum['tenor'] == 'T/N': datum['start'] = tomdate datum['maturity'] = spotdate elif datum['tenor'] == 'S/N': datum['start'] = spotdate datum['maturity'] = spotnext datum['stime'] = float(day_cf('Actual/365', v_date, datum['start'])) datum['dcf'] = float( day_cf(setting['day_count'], datum['start'], datum['maturity'])) if datum['rate'] is not None: datum['df'] = _strate_to_df(datum['rate'], datum['dcf'], ratebasis) else: datum['df'] = None datum['time'] = day_cf('Actual/365', v_date, datum['maturity']) data.append(datum) maturities = _calc_maturities(setting['start_basis'], setting['day_count'], setting['bus_day'], dates, tenors1) start_date = None if setting['start_basis'] == 'Same Day': start_date = v_date elif setting['start_basis'] == 'Tom': start_date = tomdate elif setting['start_basis'] == 'Spot': start_date = spotdate stime = day_cf('Actual/365', v_date, start_date) data_len = len(tenors1) for i in range(data_len): datum = {} datum['tenor'] = tenors1[i] datum['rate'] = rates1[i] datum['maturity'] = maturities[i] datum['start'] = start_date datum['stime'] = stime datum['dcf'] = float( day_cf(setting['day_count'], datum['start'], datum['maturity'])) if datum['rate'] is not None: datum['df'] = _strate_to_df(datum['rate'], datum['dcf'], ratebasis) else: datum['df'] = None datum['time'] = float( day_cf('Actual/365', datum['start'], datum['maturity'])) data.append(datum) # Calculate df to current date data_len = len(data) if setting['start_basis'] == 'Same Day': for i in range(data_len): datum = data[i] if datum['tenor'] == 'O/N': if datum.get('df') is not None: datum['df_tocdate'] = datum['df'] else: datum['df_tocdate'] = None elif datum['tenor'] == 'T/N' and datum.get('df') is not None: on = dict(data[i - 1]) if on.get('df') is not None: datum['df_tocdate'] = on['df'] * datum['df'] else: on['rate'] = datum['rate'] on['df'] = 1 / (1 + on['rate'] * 0.01 * on['dcf']) datum['df_tocdate'] = on['df'] * datum['df'] elif datum['tenor'] == 'S/N' and datum.get('df') is not None: tn = dict(data[i - 1]) if tn.get('df_tocdate') is not None: datum['df_tocdate'] = tn['df_tocdate'] * datum['df'] else: on = dict(data[i - 2]) on['rate'] = datum['rate'] on['df'] = 1 / (1 + on['rate'] * 0.01 * on['dcf']) on['df_tocdate'] = on['df'] tn['rate'] = datum['rate'] tn['df'] = 1 / (1 + tn['rate'] * 0.01 * tn['dcf']) tn['df_tocdate'] = on['df'] * tn['df'] datum['df_tocdate'] = tn['df'] * datum['df'] else: datum['df_tocdate'] = datum['df'] elif setting['start_basis'] == 'Spot': _adjustdf_fromspot(data) elif setting['start_basis'] == 'Tom': df_totom = None ondf = None ontime = None for i in range(0, 1, 1): datum = data[i] if datum['tenor'] == 'O/N': ondf = datum['df'] ontime = datum['time'] if ondf is not None: datum['df_tocdate'] = ondf tomtime = ontime if ondf is None: df_totom = ondf # get discount factor to interpolate x_axis = [0] y_axis = [1] for n in range(1, data_len, 1): datum1 = data[n] if datum1['df'] is not None: x_axis.append(datum1['time']) y_axis.append(datum1['df']) if ondf is None: x_value = tomtime df_totom = interpolation(x_axis, y_axis, x_value) for i in range(1, data_len, 1): datum = data[i] if datum['df'] is not None: datum['df_tocdate'] = datum['df'] * df_totom return data
def day_count_factor(convention, prev_date, current_date, bondmat_date=None, next_coupon_date=None, business_day=None, Frequency=1): if convention in _a365: return _dcf_a365(prev_date, current_date) elif convention in _a360: return _dcf_a360(prev_date, current_date) elif convention in _a365f: return _dcf_a365f(dt64(prev_date, 'D'), dt64(current_date, 'D')) elif convention in _a364: return _dcf_a364(prev_date, current_date) elif convention in _a365l: return _dcf_a365l(prev_date, current_date, Frequency=Frequency) elif (convention in _a30_360 or convention in _o30_360bb or convention in _o30_360us or convention in _a30e_360 or convention in _a30e_360i): year1 = np.datetime64(prev_date, 'Y') month1 = np.datetime64(prev_date, 'M') d1 = np.int32(prev_date - np.datetime64(month1)) + 1 m1 = np.int32(month1 - np.datetime64(year1, 'M')) + 1 y1 = np.int64(year1) year2 = np.datetime64(current_date, 'Y') month2 = np.datetime64(current_date, 'M') d2 = np.int32(current_date - np.datetime64(month2)) + 1 m2 = np.int32(month2 - np.datetime64(year2, 'M')) + 1 y2 = np.int64(year2) if convention in _o30_360bb: d1 = min(d1, 30) if d1 >= 30: d2 = min(d2, 30) return _dcf_360(d1, m1, y1, d2, m2, y2) elif convention in _o30_360us: str_date1 = str(prev_date) str_date2 = str(current_date) arr_date1 = str_date1.split('-') arr_date2 = str_date2.split('-') date1m = np.datetime64(prev_date, 'M') days = np.int32(prev_date - np.datetime64(date1m, 'D')) addmonth = date1m + 1 nextmonth = np.datetime64(addmonth, 'D') + days daysinamonth1 = np.int32(nextmonth - prev_date) date2m = np.datetime64(current_date, 'M') days2 = np.int32(current_date - np.datetime64(date2m, 'D')) addmonth2 = date2m + 1 nextmonth2 = np.datetime64(addmonth2, 'D') + days2 daysinamonth2 = np.int32(nextmonth2 - current_date) if (np.int32(arr_date1[1]) == 2 and np.int32(arr_date2[1]) == 2 and days == daysinamonth1 and days2 == daysinamonth2 and business_day == 'EOM'): d2 = 30 if (np.int32(arr_date1[1]) == 2 and days == daysinamonth1 and business_day == 'End of Month'): d1 = 30 if d2 == 31 and d1 >= 30: d2 = 30 if d1 == 31: d1 = 30 return _dcf_360(d1, m1, y1, d2, m2, y2) elif convention in _a30e_360: if d1 == 31: d1 = 30 if d2 == 31: d2 = 30 value = _dcf_360(d1, m1, y1, d2, m2, y2) return value elif convention in _a30e_360i: daysinamonth = daysinthemonth(prev_date) monthend = np.datetime64(month1, 'D') + daysinamonth - 1 if monthend == prev_date: d1 = 30 daysinamonth2 = daysinthemonth(current_date) monthend2 = np.datetime64(month2, 'D') + daysinamonth2 - 1 if bondmat_date != monthend2 and m2 != 2: d2 = 30 return _dcf_360(d1, m1, y1, d2, m2, y2) elif convention in _A_A_icma: if next_coupon_date is None: return None else: accrued_days = _datediff(prev_date, current_date).astype('int') coupon_days = _datediff(prev_date, next_coupon_date).astype('int') dcf = accrued_days / (Frequency * coupon_days) return dcf elif convention in _A_A_isda: return _dcf_a365(prev_date, current_date, business_day=business_day, Frequency=Frequency) elif convention in _A_A_afb: return "In development"
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
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 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)
@author: RMS671214 """ import time from numpy import datetime64 as dt64 from nbutils.curvebootstrapping import df_st # %% rate_basis = "Simple" day_count = "Actual/365" bus_day = "No Adjustment" rate = { 'O/N': 2.30, '1W': 2.35, '1M': 2.45, '3M': 2.55, '6M': 2.65, '12M': 2.75 } date = dt64('2021-01-01', 'D') x = 1000 rates = [rate for i in range(x)] dates = [date for i in range(x)] print(f"rates size is {len(rates)}") start0 = time.perf_counter() dfs = df_st(dates, rates, day_count, bus_day) end0 = time.perf_counter() print(f"Time taken is {end0-start0} seconds. Size is {len(dfs)}")
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
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Fri Oct 30 13:36:21 2020 @author: RMS671214 """ from nbutils.nbcurves import generate_fulldf as gen_df from numpy import datetime64 as dt64 import time # %% vdates = [dt64('2020-10-30')] st_busday = 'Modified Following' st_ratebasis = 'Simple' st_daycount = 'Actual/365' lt_busday = "No Adjustment" lt_frequency = "Semi-Annual" lt_daycount = "Actual/Actual" st_curves = [{'O/N': 2.30, '1W': 2.35, '1M': 2.45, '3M': 2.55, '6M': 2.65}] lt_curves = [{'1Y': 2.70, '2Y': 2.80, '3Y': 2.90, '5Y': 3.00, '10Y': 3.10, '30Y': 3.25}] start0 = time.perf_counter() for i in range(100): dfs = gen_df(vdates, st_curves, st_daycount, st_busday, st_ratebasis, lt_curves, lt_daycount,
structures = list(fixbond_structures(mybond)) print("STRUCTURES") print("===========") print(structures) try: import pandas as pd pd1 = pd.DataFrame(structures) except: pass # %% testdata = { "value_date": dt64("2020-05-01"), "maturity": dt64("2025-10-01"), "day_count": "Actual/Actual", "frequency": "Semi-Annual", "business_day": "No Adjustment", "date_generation": "Backward from maturity date", "face_value": 10000000, "coupon": 2.00, "ytm": 2.00 } val = fixbond(testdata) print(val["risks"]) try: pd3 = pd.DataFrame(val["structure"])
import numba import time from numpy import datetime64 as dt64, timedelta64 as td64 import numpy sdate1 = '2021-01-01' sdate2 = '2022-01-01' date1 = dt64('2021-01-01') date2 = dt64('2022-01-01') nbdate1 = numba.types.NPDatetime('D')(date1) nbdate2 = numba.types.NPDatetime('D')(date2) print(type(nbdate2)) i = 1 t = 1 def differences(date1, date2): return (date2 - date1).astype('int') / 365 start0 = time.perf_counter() for x in range(t): test1 = differences(date1, date2) end0 = time.perf_counter() print(f"Time taken is for test1 {end0-start0} seconds: result {test1}") @numba.njit(numba.types.NPTimedelta('D')(numba.types.NPDatetime('D'), numba.types.NPDatetime('D')), cache=True) def numba_differences2(date1, date2):