def __init__(self, cash_flows: Collection[pd.Series], rates: Optional[Union[float, Collection[float]]] = None, prices: Optional[Union[float, Collection[float]]] = None, day_count_convention: str = 'bus/252', calendar: str = 'cdr_anbima', ref_date: Date = TODAY): msg = 'Parameters rates and prices cannot be both None!' assert rates is not None or prices is not None, msg msg = 'Parameter cash_flows needs to be a Collection of Pandas Series!' assert all(map(lambda x: isinstance(x, pd.Series), cash_flows)), msg if rates is not None and isinstance(rates, float): rates = [rates] if prices is not None and isinstance(prices, float): prices = [prices] if rates is not None and prices is not None: msg = 'Both parameters rates and prices given, dropping prices!' warnings.warn(msg) prices = None self.ref_date = ref_date self.dc = DayCounts(dc=day_count_convention, calendar=calendar) self.zero_curve = self._initial_zero_curve(cash_flows=cash_flows, rates=rates, prices=prices, dc=self.dc, ref_date=self.ref_date) self.bootstrap(cash_flows=cash_flows, rates=rates, prices=prices)
def swap_fixed_leg_pv(today, rate, busdays, calendartype, maturity=10, periodcupons=6, notional=1000000): global zero_curve dc1 = DayCounts(busdays, calendar=calendartype) today = pd.to_datetime(today) date_range = pd.date_range(start=today, end=today + DateOffset(years=maturity), freq=DateOffset(months=periodcupons)) date_range = dc1.modified_following(date_range) df = pd.DataFrame(data=date_range[:-1], columns=['Accrual Start']) df['Accrual End'] = date_range[1:] df['days'] = (df['Accrual End'] - df['Accrual Start']).dt.days df['Notional'] = notional df['Principal'] = 0 lastline = df.tail(1) df.loc[lastline.index, 'Principal'] = notional df['Payment'] = (df['days'] / 360) * rate * df['Notional'] df['Cash Flow'] = df['Payment'] + df['Principal'] df['Cumulative Days'] = df['days'].cumsum() days = pd.DataFrame(index=df['Accrual End']) zero_curve_discount = pd.concat([zero_curve, days], sort=True).sort_index() zero_curve_discount = zero_curve_discount.interpolate( method='linear', axis=0, limit=None, inplace=False, limit_direction='forward', limit_area=None, downcast=None) zero_curve_discount = zero_curve_discount.drop(index=zero_curve.index) zero_curve_discount = pd.DataFrame(data=zero_curve_discount.values) df['zero_curve_discount'] = zero_curve_discount / 100 df['Discount'] = 1 / ( 1 + (df['zero_curve_discount'] * df['Cumulative Days'] / 360)) df['Present Value'] = (df['Cash Flow'] * df['Discount']) fixed = np.sum(df['Present Value']) return fixed
def __init__(self, prices: Union[float, Collection[float]], cash_flows: Union[pd.Series, Collection[pd.Series]], day_count_convention: str = 'bus/252', calendar: str = 'cdr_anbima', ref_date: Date = TODAY, lambdas: Optional[np.array] = ANBIMA_LAMBDAS): if isinstance(prices, float): prices = [prices] if isinstance(cash_flows, pd.Series): cash_flows = [cash_flows] self.ref_date = ref_date self.dc = DayCounts(dc=day_count_convention, calendar=calendar) self.lambdas = np.ones(2) if lambdas is None else lambdas self.betas = self.estimate_betas(prices=prices, cash_flows=cash_flows, dc=self.dc, ref_date=self.ref_date, lambdas=self.lambdas)
from bloomberg import BBG import pandas as pd import numpy as np from datetime import datetime, timedelta from calendars import DayCounts dc = DayCounts(dc='bus/252', calendar='anbima') start_date = "28-aug-1997" end_date = '28-jun-2019' bbg = BBG() try: df2 = bbg.fetch_series(securities=['LLU99 Comdty', 'LLV99 Comdty'], fields=['LAST_PRICE'], startdate=start_date, enddate=end_date) except KeyError as e: pass d_c1 = df2['LLU99 Comdty'].dropna().index[-1][1].to_pydatetime() d_c2 = df2.index[-1][1].to_pydatetime() res = dc.days(pd.to_datetime(start_date), d_c1) print(res) res = dc.days(pd.to_datetime(start_date), d_c2) print(res)
from bloomberg import BBG import pandas as pd import numpy as np from datetime import datetime, timedelta from calendars import DayCounts dc = DayCounts(dc='bus/252', calendar='anbima') d1 = pd.to_datetime('2015-01-01') res = dc.eom_preceding(d1) # pega fim do mes res = dc.isbus(d1) # verifica se é bd res = dc.preceding(d1) # pegar um bd anterior letras = ['F', 'G', 'H', 'J', 'K', 'M', 'N', 'Q', 'U', 'V', 'X', 'Z'] meses = [ 'jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec' ] def build_ticker(commodity, month, year): if month <= 12: letra = letras[month - 1] y = year % 100 year_2d = f"{y:02d}" else: letra = letras[(month - 1) % 12] y = (year + month // 12) % 100 year_2d = f"{y:02d}" return commodity + letra + year_2d + " Comdty"
import pandas as pd from tqdm import tqdm from calendars import DayCounts dc = DayCounts('BUS/252', calendar='anbima') # BW path # file_path = r'C:\Users\gamarante\Dropbox\Aulas\Insper - Financas Quantitativas\VNA Raw.xlsx' # macbook path file_path = r'/Users/gusamarante/Dropbox/Aulas/Insper - Financas Quantitativas/VNA Raw.xlsx' df_mensal = pd.read_excel(file_path, 'Mensal', index_col=0) df_diario = pd.read_excel(file_path, 'Diario', index_col=0, na_values=['#N/A N/A']) df_release = pd.read_excel(file_path, 'Release') df_release.columns = ['Date', 'IPCA'] df = pd.DataFrame(index=pd.date_range('2003-03-18', 'today', freq='D'), columns=['dia util', 'ultima virada', 'DU desde virada', 'DU entre viradas', 'time fraction', 'proj anbima', 'saiu IPCA', 'ultimo IPCA', 'proj IPCA', 'VNA']) df.index.name = 'Date' df['dia util'] = dc.isbus(df.index) # TODO com certeza existe um meio mais eficiente de fazer isso for d in tqdm(df.index, 'Filling "ultima virada"'): if d.day >= 15: df.loc[d, 'ultima virada'] = pd.datetime(d.year, d.month, 15) else: if d.month - 1 == 0: df.loc[d, 'ultima virada'] = pd.datetime(d.year-1, 12, 15)
def __init__(self, db_connect): self.conn = db_connect self.time_series = self._get_time_series() self.dc = DayCounts(dc=self.dc_convention, calendar=self.calendar)
class B3AbstractDerivative(object): monthdict = { 'F': 1, 'G': 2, 'H': 3, 'J': 4, 'K': 5, 'M': 6, 'N': 7, 'Q': 8, 'U': 9, 'V': 10, 'X': 11, 'Z': 12 } calendar = None contract = None dc_convention = None pilar_day = None roll_method = None def __init__(self, db_connect): self.conn = db_connect self.time_series = self._get_time_series() self.dc = DayCounts(dc=self.dc_convention, calendar=self.calendar) def time_menu(self, code=None): """ Gets all of the available trading dates for the contract in 'code'. If 'code' is None, all dates are returned. :param code: str with the contract code :return: list of timestamps """ if code is None: tm = self.time_series.index.levels[0] else: tm = self.time_series.index.get_loc_level(code, 1)[1] return list(tm) def market_menu(self, t=None): """ Gets all of the available contracts codes for date 't'. If 't' is None, all contract codes are returned. :param t: any format accepted by pandas.to_datetime() :return: list of contract codes """ if t is None: mm = self.time_series.index.levels[1] else: t = pd.to_datetime(t) mm = self.time_series.index.get_loc_level(t, 0)[1] return list(mm) def maturity(self, code): """ Given a contract code, returns the timestamp of the maturity date. THIS FUNCTION WILL BREAK WHEN CONTRACTS MATURE IN 2092 START TRADING :param code: str with contract code :return: tuple (year, month) """ month = self.monthdict[code.upper()[0]] year = int(code.upper()[1:]) if year >= 92: year = year + 1900 else: year = year + 2000 mat_date = date(year, month, self.pilar_day) mat_date = self.dc.busdateroll(mat_date, self.roll_method) return mat_date def du2maturity(self, t, code): """ returns the number of business days between t and the maturity date of the contract :param t: current date :param code: contract code :return: int """ mat_date = self.maturity(code) du = self.dc.days(t, mat_date) return du def dc2maturity(self, t, code): """ returns the number of actual days between t and the maturity date of the contract, independent of the daycount convention :param t: current date :param code: contract code :return: int """ mat_date = self.maturity(code) du = self.dc.daysnodc(t, mat_date) return du def volume(self, code, t=None): """ returns the trading volume. If 't' is None, returns a pandas Series of the volume of 'code'. """ if t is None: filter_contract = self.time_series.index.get_loc_level(code, 1)[0] v = self.time_series[filter_contract]['trading_volume'].droplevel( 1) else: v = self.time_series['trading_volume'].loc[t, code] return v def open_interest(self, code, t=None): """ returns the open interest at the close of date 't'. If 't' is None, returns a pandas Series of the volume of 'code'. """ if t is None: filter_contract = self.time_series.index.get_loc_level(code, 1)[0] v = self.time_series[filter_contract][ 'open_interest_close'].droplevel(1) else: v = self.time_series['open_interest_close'].loc[t, code] return v def pnl(self, code, t): # TODO implement (precisa da série do CDI, mas acho que da pra pegar com a API do SGS) pass def build_df(self): # TODO implement return def filter(self): # TODO implement pass def _get_time_series(self): """ Fetches the whole database for the given contract """ sql_query = self._time_series_query() df = pd.read_sql(sql=sql_query, con=self.conn.connection, parse_dates={'time_stamp': '%Y-%m-%d'}) df = df.set_index(['time_stamp', 'maturity_code']) return df def _time_series_query(self): sql_query = f'SELECT TIME_STAMP, MATURITY_CODE, OPEN_INTEREST_OPEN, OPEN_INTEREST_CLOSE, ' \ f'NUMBER_OF_TRADES, TRADING_VOLUME, FINANCIAL_VOLUME, PREVIOUS_SETTLEMENT, ' \ f'INDEXED_SETTLEMENT, OPENING_PRICE, MINIMUM_PRICE, MAXIMUM_PRICE, AVERAGE_PRICE, ' \ f'LAST_PRICE, SETTLEMENT_PRICE, LAST_BID, LAST_OFFER FROM "B3futures" ' \ f'WHERE CONTRACT=\'{self.contract}\' ORDER BY(TIME_STAMP, MATURITY_CODE);' return sql_query
focus_selic = [ 5.0, 5.50, 6.13, 6.50, 6.50 ] focus_ipca = [ 4.81, 5.04, 3.61, 3.25, 3.25 ] focus_selic[0] = focus_selic[0]**(126/252) selic_rates = [x/100 + 0.3477/100 for x in focus_selic] import sys sys.path.append(f'../../FinanceHub') from calendars import DayCounts import pandas as pd dc = DayCounts('BUS/252', calendar='anbima') today = pd.to_datetime('2021-04-26') d = dc.days(today, pd.to_datetime('2024-12-31')) a = (1+8.38/100)**(d/252) print(a) print(1000*a)
class CurveBootstrap(object): def __init__(self, cash_flows: Collection[pd.Series], rates: Optional[Union[float, Collection[float]]] = None, prices: Optional[Union[float, Collection[float]]] = None, day_count_convention: str = 'bus/252', calendar: str = 'cdr_anbima', ref_date: Date = TODAY): msg = 'Parameters rates and prices cannot be both None!' assert rates is not None or prices is not None, msg msg = 'Parameter cash_flows needs to be a Collection of Pandas Series!' assert all(map(lambda x: isinstance(x, pd.Series), cash_flows)), msg if rates is not None and isinstance(rates, float): rates = [rates] if prices is not None and isinstance(prices, float): prices = [prices] if rates is not None and prices is not None: msg = 'Both parameters rates and prices given, dropping prices!' warnings.warn(msg) prices = None self.ref_date = ref_date self.dc = DayCounts(dc=day_count_convention, calendar=calendar) self.zero_curve = self._initial_zero_curve(cash_flows=cash_flows, rates=rates, prices=prices, dc=self.dc, ref_date=self.ref_date) self.bootstrap(cash_flows=cash_flows, rates=rates, prices=prices) @staticmethod def _initial_zero_curve(cash_flows: Collection[pd.Series], rates: Optional[Collection[float]] = None, prices: Optional[Collection[float]] = None, dc: Optional[DayCounts] = None, ref_date: Optional[Date] = None) -> pd.Series: if rates is not None: bonds = zip(cash_flows, rates) isrates = True if prices is not None: msg = 'Both parameters rates and prices given, dropping prices!' warnings.warn(msg) del prices else: bonds = zip(cash_flows, prices) isrates = False ytm = [] curve = [] for cf, y in bonds: if len(cf) == 1: d = pd.to_datetime(cf.index[-1]).date() t = dc.tf(ref_date, d) if isrates: r = float(y) else: r = (cf.iloc[-1] / float(y))**(1. / t) - 1. ytm += [t] curve += [r] return pd.Series(index=ytm, data=curve).sort_index() def rate_for_date(self, t: Union[float, Date]) -> float: y = flat_forward_interpolation(t=t, zero_curve=self.zero_curve, dc=self.dc, ref_date=self.ref_date) return y @staticmethod def _bond_pv_for_rate(expanded_rate: float, zero_curve: pd.Series, bond_cash_flows: pd.Series, dc: Optional[DayCounts] = None, ref_date: Optional[Date] = None) -> float: zero_curve_end = max(zero_curve.index) ytm = dc.tf(ref_date, max(bond_cash_flows.index)) expanded_point = pd.Series(index=[ytm], data=expanded_rate) zero_curve = zero_curve.append(expanded_point).sort_index().copy() pv = 0. for d, c in bond_cash_flows.items(): t = dc.tf(ref_date, d) if t > zero_curve_end: y = flat_forward_interpolation(t=t, zero_curve=zero_curve, dc=dc, ref_date=ref_date) pv += c / (1. + y)**t return pv @staticmethod def _bond_strip( zero_curve: pd.Series, bond_cash_flows: pd.Series, dc: Optional[DayCounts] = None, ref_date: Optional[Date] = None, rate: Optional[float] = None, price: Optional[float] = None) -> Tuple[float, float, pd.Series]: msg = 'Parameters rate and price cannot be both None!' assert rate is not None or price is not None, msg msg = 'Bond maturity is shorter than given zero curve!' maturity = dc.tf(ref_date, max(bond_cash_flows.index)) zero_curve_end = max(zero_curve.index) assert zero_curve_end <= maturity, msg if price is not None: price = float(price) if rate is not None: msg = 'Both parameters rate and price given, dropping rate!' warnings.warn(msg) del rate else: price = 0. for d, c in bond_cash_flows.items(): ytm = dc.tf(ref_date, d) price += c / (1. + rate)**ytm pv = 0. for d, c in bond_cash_flows.sort_index().items(): t = dc.tf(ref_date, d) if t <= zero_curve_end: y = flat_forward_interpolation(t=t, zero_curve=zero_curve, dc=dc, ref_date=ref_date) pv += c / (1. + y)**t return price, pv, maturity def _expand_zero_curve(self, bond_cash_flows: pd.Series, rate: Optional[float] = None, price: Optional[float] = None): price, pv, ytm = self._bond_strip(zero_curve=self.zero_curve, bond_cash_flows=bond_cash_flows, dc=self.dc, ref_date=self.ref_date, rate=rate, price=price) price_given_rate = lambda x: self._bond_pv_for_rate( expanded_rate=x, zero_curve=self.zero_curve, bond_cash_flows=bond_cash_flows, dc=self.dc, ref_date=self.ref_date) + pv pct_error = lambda x: (price - price_given_rate(x)) / price obj_fun = lambda x: pct_error(x)**2. x0 = self.zero_curve.iloc[-1] res = opt.minimize(obj_fun, x0, method='SLSQP') if not res['message'] == 'Optimization terminated successfully.': raise ArithmeticError('Optimization convergence may have failed!') expanded_point = pd.Series(index=[ytm], data=res.x) return self.zero_curve.append(expanded_point).sort_index() def bootstrap(self, cash_flows: Collection[pd.Series], rates: Optional[Collection[float]] = None, prices: Optional[Collection[float]] = None): msg = 'Parameters rates and prices cannot be both None!' assert rates is not None or prices is not None, msg tmax = max(self.zero_curve.index) for i in range(len(cash_flows)): cf = list(cash_flows)[i] if len(cf) > 1 and self.dc.tf(self.ref_date, max(cf.index)) > tmax: if rates is None or list(rates)[i] is None: new_curve = self._expand_zero_curve(bond_cash_flows=cf, rate=None, price=list(prices)[i]) elif prices is None or list(prices)[i] is None: new_curve = self._expand_zero_curve(bond_cash_flows=cf, rate=list(rates)[i], price=None) else: continue self.zero_curve = new_curve else: continue
# d1 = pd.to_datetime('2015-01-01') # d2 = pd.to_datetime('2019-07-07') # Case 2 - d1 is a collection and d2 is a Timestamp # d1 = pd.date_range('2019-01-01', '2019-12-31', freq='M') # d2 = pd.to_datetime('2019-07-07') # Case 3 - d1 is a Timestamp and d2 is a collection # d1 = pd.to_datetime('2019-07-07') # d2 = pd.date_range('2019-01-01', '2019-12-31', freq='M') # Case 4 - d1 and d2 are collections d1 = pd.date_range('2015-01-01', '2015-12-31', freq='M') d2 = pd.date_range('2019-01-01', '2019-12-31', freq='M') dc = DayCounts(dc='bus/252', calendar='anbima') print('tf - Time Fraction') res = dc.tf(d1, d2) print(res, '\n') print('days - Number of days') res = dc.days(d1, d2) print(res, '\n') print('Adjust - ????') res = dc.adjust(d1) print(res, '\n') print('daysnodc - Actual number of days, irrespective of daycount')
import pandas as pd from tqdm import tqdm from calendars import DayCounts dc = DayCounts('BUS/252', calendar='anbima') # BW path # file_path = r'C:\Users\gamarante\Dropbox\Aulas\Insper - Financas Quantitativas\VNA Raw.xlsx' # macbook path # file_path = r'/Users/gusamarante/Dropbox/Aulas/Insper - Financas Quantitativas/VNA Raw.xlsx' # mac path file_path = r'/Users/gustavoamarante/Dropbox/Aulas/Insper - Financas Quantitativas/VNA Raw.xlsx' df_mensal = pd.read_excel(file_path, 'Mensal', index_col=0) df_diario = pd.read_excel(file_path, 'Diario', index_col=0, na_values=['#N/A N/A']) df_release = pd.read_excel(file_path, 'Release') df_release.columns = ['Date', 'IPCA'] df = pd.DataFrame(index=pd.date_range('2003-03-18', 'today', freq='D'), columns=[ 'dia util', 'ultima virada', 'DU desde virada', 'DU entre viradas', 'time fraction', 'proj anbima', 'saiu IPCA', 'ultimo IPCA', 'proj IPCA', 'ultimo index', 'VNA' ]) df.index.name = 'Date'
class FwdIRSTrackers(object): """ Class for creating excess return indices for rolling interest rate swaps using data from bloomberg. At the start date, we assume we trade 100 notional 1M forward starting swaps with user provided maturity. We mark-to-market the position over the month and then roll it into the new 1M forward at the end of the month """ # TODO: Include more currencies currency_bbg_code_dict = { 'AUD': 'AD', 'CAD': 'CD', 'CHF': 'SF', 'EUR': 'EU', 'GBP': 'BP', 'JPY': 'JY', 'NZD': 'ND', 'SEK': 'SK', 'USD': 'US' } # TODO: Check these are correct currency_calendar_dict = { 'USD': DayCounts('30E/360 ISDA', calendar='us_trading'), 'AUD': DayCounts('ACT/365', calendar='us_trading'), 'CAD': DayCounts('ACT/365', calendar='us_trading'), 'CHF': DayCounts('30A/360', calendar='us_trading'), 'EUR': DayCounts('30U/360', calendar='us_trading'), 'GBP': DayCounts('ACT/365', calendar='us_trading'), 'JPY': DayCounts('ACT/365F', calendar='us_trading'), 'NZD': DayCounts('ACT/365', calendar='us_trading'), 'SEK': DayCounts('30A/360', calendar='us_trading') } def __init__(self, ccy='USD', tenor=10, start_date='2004-01-05', end_date='today'): """ Returns an object with the following attributes: - spot_swap_rates: Series with the rate for the spot starting swaps - fwd_swap_rates: Series with the rate for the 1M forward starting swaps - er_index: Series with the excess return index :param ccy_symbol: str, Currency symbol :param tenor: int, Tenor for the swap :param start_date: str, when the tracker should start :param end_date: str, when the tracker should end """ self.ccy = ccy self.tenor = tenor self.start_date = (pd.to_datetime(start_date) + BDay(1)).date() self.end_date = pd.to_datetime(end_date).date() self.dc = self.currency_calendar_dict[ccy] self.bbg = BBG() self.spot_swap_rates = self._get_spot_swap_rates() self.fwd_swap_rates = self._get_1m_fwd_swap_rates() self.df_tracker = self._calculate_tr_index() self.country = self.bbg.fetch_contract_parameter( self.ticker_spot, 'COUNTRY_ISO').iloc[0, 0].upper() self.exchange_symbol = self.bbg.fetch_contract_parameter( self.ticker_fwd, 'TICKER').iloc[0, 0] self.fh_ticker = 'irs ' + self.country.lower() + ' ' + self.ccy.lower() self.df_metadata = pd.DataFrame(data={ 'fh_ticker': self.fh_ticker, 'asset_class': 'fixed income', 'type': 'swap', 'currency': self.ccy.upper(), 'country': self.country.upper(), 'maturity': self.tenor, 'roll_method': '1 month' }, index=[self.ticker_spot]) self.df_tracker = self._get_tracker_melted() def _calculate_tr_index(self): spot_rate_series = self.spot_swap_rates.iloc[:, 0].dropna() fwd_rate_series = self.fwd_swap_rates.iloc[:, 0].dropna() ts_df = pd.concat([ spot_rate_series.to_frame('spot'), fwd_rate_series.to_frame('fwd_1m') ], axis=1, sort=True).fillna(method='ffill').dropna() er_index = pd.Series(index=ts_df.index) er_index.iloc[0] = 100 start_date = er_index.index[0] fwd_swap_rate = ts_df.loc[start_date, 'fwd_1m'] / 100 ref_swap_rate_d_minus_1 = fwd_swap_rate roll_date = self.dc.busdateroll(start_date + relativedelta(months=1), 'modifiedfollowing') fwd_mat = self.dc.busdateroll( start_date + relativedelta(months=1 + self.tenor * 12), 'modifiedfollowing') for d in er_index.index[1:]: current_fwd_swap_rate = ts_df.loc[d, 'fwd_1m'] / 100 current_spot_swap_rate = ts_df.loc[d, 'spot'] / 100 curr_fwd_mat = self.dc.busdateroll( d + relativedelta(months=1 + self.tenor * 12), 'modifiedfollowing') curr_spot_mat = self.dc.busdateroll( d + relativedelta(months=self.tenor * 12), 'modifiedfollowing') w = 1 if d >= roll_date else self.dc.tf( curr_fwd_mat, fwd_mat) / self.dc.tf(curr_fwd_mat, curr_spot_mat) ref_swap_rate = w * current_spot_swap_rate + ( 1 - w) * current_fwd_swap_rate # This is the present value of a basis point # Using the interpolated forward swap rate as an internal rate of return pv01 = np.sum([(1 / 2) * ((1 + ref_swap_rate / 2)**(-i)) for i in range(1, self.tenor * 2 + 1)]) if np.isnan((ref_swap_rate_d_minus_1 - ref_swap_rate) * pv01): ret = 0 else: ret = (ref_swap_rate_d_minus_1 - ref_swap_rate) * pv01 er_index[d] = er_index[:d].iloc[-2] * (1 + ret) if d >= roll_date: roll_date = self.dc.busdateroll(d + relativedelta(months=1), 'modifiedfollowing') fwd_mat = self.dc.busdateroll( d + relativedelta(months=1 + self.tenor * 12), 'modifiedfollowing') ref_swap_rate_d_minus_1 = ts_df.loc[d, 'fwd_1m'] / 100 else: ref_swap_rate_d_minus_1 = ref_swap_rate return er_index.to_frame('er_index') def _get_spot_swap_rates(self): if self.ccy == 'EUR': ticker = self.currency_bbg_code_dict[self.ccy] + 'SA' + str( self.tenor) + ' Curncy' elif self.ccy == 'NZD': ticker = self.currency_bbg_code_dict[self.ccy] + 'SWAP' + str( self.tenor) + ' Curncy' else: # The AUD is using the 6M floating leg case as default to be consistent with the forward ticker # The correct ticker is ADSW10Q Curncy 3M floating leg but then # you cannot use S0302FS 1M10Y BLC Curncy for the forward swap ticker = self.currency_bbg_code_dict[self.ccy] + 'SW' + str( self.tenor) + ' Curncy' bbg_raw_spot_data = self.bbg.fetch_series(securities=ticker, fields='PX_LAST', startdate=self.start_date, enddate=self.end_date) self.ticker_spot = ticker bbg_raw_spot_data.columns = [self.ccy + str(self.tenor) + 'Y'] bbg_raw_spot_data.index = pd.to_datetime(bbg_raw_spot_data.index) return bbg_raw_spot_data.dropna(how='all') def _get_1m_fwd_swap_rates(self): if self.ccy == 'EUR': ticker = self.currency_bbg_code_dict[self.ccy] + 'SAA' + str( self.tenor).zfill(2) + ' Curncy' elif self.ccy == 'GBP': ticker = self.currency_bbg_code_dict[self.ccy] + 'SWA' + str( self.tenor).zfill(2) + ' Curncy' elif self.ccy == 'AUD': # The AUD is using the 6M floating leg case as default to be consistent with the forward ticker # The correct ticker is ADSW10Q Curncy 3M floating leg but then # you cannot use S0302FS 1M10Y BLC Curncy for the forward swap ticker = 'S0302FS 1M' + str(self.tenor) + 'Y BLC Curncy' elif self.ccy == 'USD': ticker = self.currency_bbg_code_dict[self.ccy] + 'FS0A' + str( self.tenor) + ' Curncy' else: ticker = self.currency_bbg_code_dict[self.ccy] + 'FS0A' + str( self.tenor).zfill(2) + ' Curncy' bbg_fwd_data = self.bbg.fetch_series(securities=ticker, fields='PX_LAST', startdate=self.start_date, enddate=self.end_date) self.ticker_fwd = ticker bbg_fwd_data.columns = [self.ccy + str(self.tenor) + 'Y'] bbg_fwd_data.index = pd.to_datetime(bbg_fwd_data.index) return bbg_fwd_data.dropna(how='all') def _get_tracker_melted(self): df = self.df_tracker[['er_index' ]].rename({'er_index': self.ticker_spot}, axis=1) df['time_stamp'] = df.index.to_series() df = df.melt(id_vars='time_stamp', var_name='fh_ticker', value_name='value') df = df.dropna() return df
""" Author: Gustavo Soares """ import warnings from typing import Optional import pandas as pd import numpy as np from calendars import DayCounts from calendars.custom_date_types import Date, TODAY from scipy import optimize dc = DayCounts('bus/252', calendar='cdr_anbima') def truncate(number, decimals=0): """Returns a value truncated to a specific number of decimal places""" if not isinstance(decimals, int): raise TypeError("decimal places must be an integer.") elif decimals < 0: raise ValueError("decimal places has to be 0 or more.") elif decimals == 0: return np.trunc(number) factor = 10.0**decimals return np.trunc(number * factor) / factor class LTN(object): def __init__(self, expiry: Date, rate: Optional[float] = None, price: Optional[float] = None,