From 05a09fda8ab1dd5901ec4bcaf26dca8c814a3f36 Mon Sep 17 00:00:00 2001 From: jeroen Date: Wed, 22 Feb 2023 16:04:53 +0100 Subject: [PATCH] Almost there.. --- openbb_terminal/fixedincome/ecb_view.py | 1 - .../fixedincome/fixedincome_controller.py | 127 +++++++++- openbb_terminal/fixedincome/fred_model.py | 56 ++++- openbb_terminal/fixedincome/fred_view.py | 234 +++++++++++++++++- .../miscellaneous/data_sources_default.json | 6 + openbb_terminal/miscellaneous/i18n/en.yml | 10 +- 6 files changed, 403 insertions(+), 31 deletions(-) diff --git a/openbb_terminal/fixedincome/ecb_view.py b/openbb_terminal/fixedincome/ecb_view.py index 7e3951bd772..4714df7f5b0 100644 --- a/openbb_terminal/fixedincome/ecb_view.py +++ b/openbb_terminal/fixedincome/ecb_view.py @@ -144,7 +144,6 @@ def display_ecb_yield_curve( export : str Export data to csv,json,xlsx or png,jpg,pdf,svg file """ - print(date) rates, date_of_yield = ecb_model.get_ecb_yield_curve( date, yield_type, True, detailed, any_rating ) diff --git a/openbb_terminal/fixedincome/fixedincome_controller.py b/openbb_terminal/fixedincome/fixedincome_controller.py index d50260f53f8..12cc6d1d43e 100644 --- a/openbb_terminal/fixedincome/fixedincome_controller.py +++ b/openbb_terminal/fixedincome/fixedincome_controller.py @@ -47,8 +47,10 @@ class FixedIncomeController(BaseController): "ecbycrv", "usrates", "icebofa", + "moody", "cp", "spot", + "hqm", "icespread", "tbffr", ] @@ -171,7 +173,7 @@ class FixedIncomeController(BaseController): def print_help(self): """Print help""" - mt = MenuText("fixedincome/") + mt = MenuText("fixedincome/", column_sources=120) mt.add_info("_reference_rates_") mt.add_cmd("estr") mt.add_cmd("sofr") @@ -193,8 +195,10 @@ class FixedIncomeController(BaseController): mt.add_raw("\n") mt.add_info("_corporate_bonds_") mt.add_cmd("icebofa") + mt.add_cmd("moody") mt.add_cmd("cp") mt.add_cmd("spot") + mt.add_cmd("hqm") mt.add_raw("\n") mt.add_info("_spreads_") mt.add_cmd("icespread") @@ -1197,6 +1201,70 @@ class FixedIncomeController(BaseController): if ns_parser.sheet_name else None, ) + + @log_start_end(log=logger) + def call_moody(self, other_args: List[str]): + """Process moody command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="moody", + description="Moody's Aaa and Baa are investment bonds that acts as an index of " + "the performance of all bonds given an Aaa or Baa rating by Moody's Investors Service respectively" + "These corporate bonds often are used in macroeconomics as an alternative to the federal ten-year " + "Treasury Bill as an indicator of the interest rate." + ) + + parser.add_argument( + "-t", + "--type", + dest="data_type", + type=str, + help="What type you'd like to collect data for", + default="aaa", + choices=fred_view.MOODY_TO_OPTIONS['Type'].keys() + ) + + parser.add_argument( + "--spread", + dest="spread", + type=str, + help="Whether you want to show the spread", + default=None, + choices=fred_view.MOODY_TO_OPTIONS['Spread'] + ) + + parser.add_argument( + "-s", + "--start", + dest="start_date", + type=valid_date, + help="Starting date (YYYY-MM-DD) of data", + default="1980-01-01", + ) + parser.add_argument( + "-e", + "--end", + dest="end_date", + type=valid_date, + help="Ending date (YYYY-MM-DD) of data", + default=None, + ) + ns_parser = self.parse_known_args_and_warn( + parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True + ) + if ns_parser: + fred_view.plot_moody( + data_type=ns_parser.data_type, + spread=ns_parser.spread, + start_date=ns_parser.start_date, + end_date=ns_parser.end_date, + raw=ns_parser.raw, + export=ns_parser.export, + sheet_name=" ".join(ns_parser.sheet_name) + if ns_parser.sheet_name + else None, + ) @log_start_end(log=logger) @@ -1206,8 +1274,10 @@ class FixedIncomeController(BaseController): add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="cp", - description="Plot various rates from the United States. This includes tbill (Treasury Bills), " - "Constant Maturity treasuries (cmn) and Inflation Protected Treasuries (TIPS)", + description="Commercial paper (CP) consists of short-term, promissory notes " + "issued primarily by corporations. Maturities range up to 270 days but average " + "about 30 days. Many companies use CP to raise cash needed for current " + "transactions, and many find it to be a lower-cost alternative to bank loans." ) parser.add_argument( @@ -1306,8 +1376,10 @@ class FixedIncomeController(BaseController): add_help=False, formatter_class=argparse.ArgumentDefaultsHelpFormatter, prog="spot", - description="Plot various rates from the United States. This includes tbill (Treasury Bills), " - "Constant Maturity treasuries (cmn) and Inflation Protected Treasuries (TIPS)", + description="The spot rate for any maturity is the yield on a bond that provides " + "a single payment at that maturity. This is a zero coupon bond. Because each " + "spot rate pertains to a single cashflow, it is the relevant interest rate " + "concept for discounting a pension liability at the same maturity." ) parser.add_argument( @@ -1372,6 +1444,51 @@ class FixedIncomeController(BaseController): if ns_parser.sheet_name else None, ) + + @log_start_end(log=logger) + def call_hqm(self, other_args: List[str]): + """Process hqdm command""" + parser = argparse.ArgumentParser( + add_help=False, + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + prog="hqm", + description="The HQM yield curve represents the high quality corporate bond market, i.e., " + "corporate bonds rated AAA, AA, or A. The HQM curve contains two regression terms. These " + "terms are adjustment factors that blend AAA, AA, and A bonds into a single HQM yield curve " + "that is the market-weighted average (MWA) quality of high quality bonds.", + ) + + parser.add_argument( + "-d", + "--date", + dest="date", + type=valid_date, + help="Define the date of the yield curve.", + default=None + ) + + parser.add_argument( + "-p", + "--par", + dest="par", + action='store_true', + help="Whether to include the Par Yield.", + default=False + ) + + ns_parser = self.parse_known_args_and_warn( + parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES, raw=True + ) + if ns_parser: + fred_view.plot_cycrv( + date=ns_parser.date.strftime("%Y-%m-%d") if ns_parser.date else "", + par=ns_parser.par, + raw=ns_parser.raw, + export=ns_parser.export, + sheet_name=" ".join(ns_parser.sheet_name) + if ns_parser.sheet_name + else None, + ) @log_start_end(log=logger) def call_icespread(self, other_args: List[str]): diff --git a/openbb_terminal/fixedincome/fred_model.py b/openbb_terminal/fixedincome/fred_model.py index d44a0b414c4..ab7b50bd383 100644 --- a/openbb_terminal/fixedincome/fred_model.py +++ b/openbb_terminal/fixedincome/fred_model.py @@ -40,9 +40,31 @@ YIELD_CURVE_SERIES_REAL = { "30Year": "DFII30", } -YIELD_CURVE_NOMINAL_RATES = [1 / 12, 0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30] -YIELD_CURVE_REAL_RATES = [5, 7, 10, 20, 30] +YIELD_CURVE_SERIES_CORPORATE_SPOT = { + "6Month": "HQMCB6MT", + "1Year": "HQMCB1YR", + "2Year": "HQMCB2YR", + "3Year": "HQMCB3YR", + "5Year": "HQMCB5YR", + "7Year": "HQMCB7YR", + "10Year": "HQMCB10YR", + "20Year": "HQMCB20YR", + "30Year": "HQMCB30YR", + "50Year": "HQMCB50YR", + "75Year": "HQMCB75YR", + "100Year": "HQMCB100YR" +} +YIELD_CURVE_SERIES_CORPORATE_PAR = { + "2Year": "HQMCB2YRP", + "5Year": "HQMCB5YRP", + "10Year": "HQMCB10YRP", + "30Year": "HQMCB30YRP", +} +YIELD_CURVE_NOMINAL_RATES = [1 / 12, 0.25, 0.5, 1, 2, 3, 5, 7, 10, 20, 30] +YIELD_CURVE_SPOT_RATES = [0.5, 1, 2, 3, 5, 7, 10, 20, 30, 50, 75, 100] +YIELD_CURVE_REAL_RATES = [5, 7, 10, 20, 30] +YIELD_CURVE_PAR_RATES = [2, 5, 10, 30] @log_start_end(log=logger) @check_api_key(["API_FRED_KEY"]) @@ -83,7 +105,7 @@ def get_series_data( @log_start_end(log=logger) @check_api_key(["API_FRED_KEY"]) def get_yield_curve( - date: str = "", return_date: bool = False, inflation_adjusted: bool = False + date: str = "", return_date: bool = False, inflation_adjusted: bool = False, spot_or_par: str = None ) -> Tuple[pd.DataFrame, str]: """Gets yield curve data from FRED. @@ -134,6 +156,7 @@ def get_yield_curve( fredapi_client = Fred(cfg.API_FRED_KEY) df = pd.DataFrame() + # Check that the date is in the past today = datetime.now().strftime("%Y-%m-%d") if date and date >= today: @@ -142,16 +165,27 @@ def get_yield_curve( return pd.DataFrame(), date return pd.DataFrame() - # Add in logic that will get the most recent date. - get_last = False - - if not date: - date = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d") + # Add in logic that will get the most recent date + if date: + get_last = False + start_date = (datetime.strptime(date, "%Y-%m-%d") - timedelta(days=50)).strftime("%Y-%m-%d") + else: + date = datetime.now().strftime("%Y-%m-%d") + start_date = (datetime.now() - timedelta(days=50)).strftime("%Y-%m-%d") get_last = True if inflation_adjusted: fred_series = YIELD_CURVE_SERIES_REAL years = YIELD_CURVE_REAL_RATES + elif spot_or_par: + if spot_or_par == "spot": + years = YIELD_CURVE_SPOT_RATES + fred_series = YIELD_CURVE_SERIES_CORPORATE_SPOT + elif spot_or_par == "par": + years = YIELD_CURVE_PAR_RATES + fred_series = YIELD_CURVE_SERIES_CORPORATE_PAR + else: + console.print("Please select either 'spot' or 'par' rates.") else: fred_series = YIELD_CURVE_SERIES_NOMINAL years = YIELD_CURVE_NOMINAL_RATES @@ -160,7 +194,7 @@ def get_yield_curve( df = pd.concat( [ df, - pd.DataFrame(fredapi_client.get_series(s_id, date), columns=[key]), + pd.DataFrame(fredapi_client.get_series(s_id, start_date), columns=[key]), ], axis=1, ) @@ -176,6 +210,10 @@ def get_yield_curve( idx = -1 if get_last else 0 date_of_yield = df.index[idx].strftime("%Y-%m-%d") rates = pd.DataFrame(df.iloc[idx, :].values, columns=["Rate"]) + + if spot_or_par: + console.print(f"Because {spot_or_par.title()} rates are published monthly, " + f"the nearest date to {date} is used which is {date_of_yield}.") else: date_of_yield = date series = df[df.index == date] diff --git a/openbb_terminal/fixedincome/fred_view.py b/openbb_terminal/fixedincome/fred_view.py index 91861161e55..8ccdfb46804 100644 --- a/openbb_terminal/fixedincome/fred_view.py +++ b/openbb_terminal/fixedincome/fred_view.py @@ -176,6 +176,27 @@ ICE_BOFA_TO_OPTIONS = { 'public_sector'] } +MOODY_TO_OPTIONS = { + "Type": {"aaa": { + 'index': { + "id": "DAAA", "name": "Moody's Seasoned Aaa Corporate Bond Yield"}, + 'treasury': { + "id": "AAA10Y", "name": "Moody's Seasoned Aaa Corporate Bond Yield Relative to Yield on 10-Year Treasury Constant Maturity"}, + "fed_funds": + {"id": "AAAFF", "name": "Moody's Seasoned Aaa Corporate Bond Minus Federal Funds Rate"}, + }, + "baa": { + 'index': { + "id": "DBAA", "name": "Moody's Seasoned Baa Corporate Bond Yield"}, + 'treasury': { + "id": "BAA10Y", "name": "Moody's Seasoned Baa Corporate Bond Yield Relative to Yield on 10-Year Treasury Constant Maturity"}, + "fed_funds": + {"id": "BAAFF", "name": "Moody's Seasoned Baa Corporate Bond Minus Federal Funds Rate"}, + }, + }, + "Spread": ['treasury', 'fed_funds'] +} + CP_TO_OPTIONS = { 'Maturity': ['15d', '30d', '60d', '7d', '90d', 'overnight'], 'Category': ['asset_backed', 'financial', 'non_financial', 'spread'], @@ -699,8 +720,9 @@ def plot_fftr( df.index, df.values, ) - ax.set_title("Federal Funds Target Range [Percent]") + ax.set_title("Federal Funds Target Range") ax.legend(["Upper limit", "Lower limit"]) + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -916,7 +938,8 @@ def plot_iorb( df.values, color=next(colors, "#FCED00"), ) - ax.set_title("Interest Rate on Reserve Balances [Percent]") + ax.set_title("Interest Rate on Reserve Balances") + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -1058,8 +1081,8 @@ def plot_dwpcr( ) ax.set_title( "Discount Window Primary Credit Rate " - + ID_TO_NAME_DWPCR[series_id] - + " [Percent]" + + ID_TO_NAME_DWPCR[series_id], + fontsize=10 ) theme.style_primary_axis(ax) @@ -1222,7 +1245,8 @@ def plot_ecbmlfr( df.values, color=next(colors, "#FCED00"), ) - ax.set_title("ECB Marginal Lending Facility Rate for Euro Area [Percent]") + ax.set_title("ECB Marginal Lending Facility Rate for Euro Area") + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -1282,8 +1306,9 @@ def plot_ecbmrofr( color=next(colors, "#FCED00"), ) ax.set_title( - "ECB Main Refinancing Operations Rate: Fixed Rate Tenders for Euro Area [Percent]" + "ECB Main Refinancing Operations Rate: Fixed Rate Tenders for Euro Area" ) + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -1348,8 +1373,11 @@ def plot_tmc( ax.set_title( "10-Year Treasury Constant Maturity Minus " + ID_TO_NAME_TMC[series_id] - + " Treasury Constant Maturity [Percent]" + + " Treasury Constant Maturity", + fontsize=10 ) + + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -1413,7 +1441,8 @@ def plot_ffrmc( ) ax.set_title( ID_TO_NAME_FFRMC[series_id] - + " Treasury Constant Maturity Minus Federal Funds Rate [Percent]" + + " Treasury Constant Maturity Minus Federal Funds Rate", + fontsize=10 ) theme.style_primary_axis(ax) @@ -1481,7 +1510,7 @@ def display_yield_curve( ax.plot(rates["Maturity"], rates["Rate"], "-o") ax.set_xlabel("Maturity") - ax.set_ylabel("Rate (%)") + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: ax.set_title( @@ -1582,7 +1611,7 @@ def plot_usrates( title = f"{maturity.replace('_', ' ').title()} Yields on Treasury inflation protected securities (TIPS) adjusted to constant maturities" ax.set_title(title, fontsize=15) - ax.set_ylabel("Yield (%})") + ax.set_ylabel("Yield (%)") theme.style_primary_axis(ax) if external_axes is None: @@ -1650,10 +1679,11 @@ def plot_tbffr( ) ax.set_title( ID_TO_NAME_TBFFR[series_id] - + " Treasury Bill Minus Federal Funds Rate [Percent]" + + " Treasury Bill Minus Federal Funds Rate" ) theme.style_primary_axis(ax) - + + ax.set_ylabel("Yield (%)") if external_axes is None: theme.visualize_output() @@ -1830,6 +1860,94 @@ def plot_icebofa( df / 100, sheet_name, ) + + +@log_start_end(log=logger) +@check_api_key(["API_FRED_KEY"]) +def plot_moody( + data_type: str = "aaa", + spread: str = None, + start_date: Optional[str] = None, + end_date: Optional[str] = None, + raw: bool = False, + export: str = "", + sheet_name: str = "", + external_axes: Optional[List[plt.Axes]] = None, +): + """Plot ICE BofA US Corporate Bond Index data. + + Parameters + ---------- + data_type: str + The type of data you want to see, either "yield", "yield_to_worst", "total_return", or "spread" + category: str + The type of category you want to see, either "all", "duration", "eur" or "usd". + area: str + The type of area you want to see, either "asia", "emea", "eu", "ex_g10", "latin_america" or "us" + grade: str + The type of grade you want to see, either "a", "aa", "aaa", "b", "bb", "bbb", "ccc", "crossover", + "high_grade", "high_yield", "non_financial", "non_sovereign", "private_sector", "public_sector" + start_date: Optional[str] + Start date, formatted YYYY-MM-DD + end_date: Optional[str] + End date, formatted YYYY-MM-DD + raw: bool + Show raw data + export: str + Export data to csv or excel file + sheet_name: str + Name of the sheet to export to + external_axes: Optional[List[plt.Axes]] + External axes (1 axis is expected in the list) + """ + series_id = MOODY_TO_OPTIONS['Type'][data_type][spread if spread else 'index']['id'] + name = MOODY_TO_OPTIONS['Type'][data_type][spread if spread else 'index']['name'] + + series = fred_model.get_series_data( + series_id=series_id, start_date=start_date, end_date=end_date + ) + + df = pd.DataFrame(series, columns=[f"{data_type}_{spread if spread else 'index'}"]) + df.index = pd.to_datetime(df.index).date + + # This plot has 1 axis + if not external_axes: + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + elif is_valid_axes_count(external_axes, 1): + (ax,) = external_axes + else: + return + + colors = cycle(theme.get_colors()) + + ax.plot( + df.index, + df.values, + color=next(colors, "#FCED00"), + ) + + ax.set_title(name, fontsize=10) + ax.set_ylabel("Yield (%)") + theme.style_primary_axis(ax) + + if external_axes is None: + theme.visualize_output() + + if raw: + print_rich_table( + df.iloc[-10:], + title=name, + show_index=True, + floatfmt=".3f", + ) + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "MOODY", + df / 100, + sheet_name, + ) @log_start_end(log=logger) @check_api_key(["API_FRED_KEY"]) @@ -1962,7 +2080,11 @@ def plot_spot( sheet_name: str = "", external_axes: Optional[List[plt.Axes]] = None, ): - """Plot ICE BofA US Corporate Bond Index data. + """ + The spot rate for any maturity is the yield on a bond that provides + a single payment at that maturity. This is a zero coupon bond. Because each + spot rate pertains to a single cashflow, it is the relevant interest rate + concept for discounting a pension liability at the same maturity. Parameters ---------- @@ -2058,4 +2180,90 @@ def plot_spot( "SPOT", df / 100, sheet_name, + ) + +@log_start_end(log=logger) +@check_api_key(["API_FRED_KEY"]) +def plot_hqm( + date: Optional[str] = None, + par: bool = False, + raw: bool = False, + export: str = "", + sheet_name: str = "", + external_axes: Optional[List[plt.Axes]] = None, +): + """ + The HQM yield curve represents the high quality corporate bond market, i.e., + corporate bonds rated AAA, AA, or A. The HQM curve contains two regression terms. These + terms are adjustment factors that blend AAA, AA, and A bonds into a single HQM yield curve + that is the market-weighted average (MWA) quality of high quality bonds. + + Parameters + ---------- + date: str + The date of the yield curve you wish to plot + par: bool + Whether you wish to plot the par yield curve as well + raw: bool + Show raw data + export: str + Export data to csv or excel file + sheet_name: str + Name of the sheet to export to + external_axes: Optional[List[plt.Axes]] + External axes (1 axis is expected in the list) + """ + df = pd.DataFrame() + data_types = ["spot", "par"] if par else ['spot'] + + for types in data_types: + subset, date_of_yield = fred_model.get_yield_curve(date, True, spot_or_par=types) + subset.set_index('Maturity', inplace=True) + subset.columns = [types] + + df = pd.concat([df, subset], axis=1) + + if par: + # Drop NaNs because of length mismatch + df = df.dropna() + + if df.empty: + console.print(f"[red]Yield data not found at {date_of_yield}.[/red]\n") + + # This plot has 1 axis + if not external_axes: + _, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI) + elif is_valid_axes_count(external_axes, 1): + (ax,) = external_axes + else: + return + + for types in data_types: + ax.plot(df.index, df[types], label=f"{types.title()} Yield") + + ax.set_title(f"Spot{'and Par' if par else ''} Yield Curve for {date_of_yield}") + ax.set_xlabel("Maturity") + ax.set_ylabel("Yield (%)") + ax.legend() if par else None + theme.style_primary_axis(ax) + + if external_axes is None: + theme.visualize_output() + + if raw: + print_rich_table( + df, + headers=list(df.columns), + show_index=True, + index_name="Maturity", + title=f"Spot {'and Par' if par else ''} Yield Curve for {date_of_yield}", + floatfmt=".3f", + ) + + export_data( + export, + os.path.dirname(os.path.abspath(__file__)), + "cycrv", + df / 100, + sheet_name, ) \ No newline at end of file diff --git a/openbb_terminal/miscellaneous/data_sources_default.json b/openbb_terminal/miscellaneous/data_sources_default.json index abd083e00c0..ce627503055 100644 --- a/openbb_terminal/miscellaneous/data_sources_default.json +++ b/openbb_terminal/miscellaneous/data_sources_default.json @@ -505,6 +505,12 @@ "usrates": ["FRED"], "ycrv": ["FRED"], "ecbycrv": ["ECB"], + "icebofa": ["FRED"], + "moody": ["FRED"], + "spot": ["FRED"], + "cp": ["FRED"], + "hqm": ["FRED"], + "icespread": ["FRED"], "tmc": ["FRED"], "ffrmc": ["FRED"], "tbffr": ["FRED"] diff --git a/openbb_terminal/miscellaneous/i18n/en.yml b/openbb_terminal/miscellaneous/i18n/en.yml index 3e0fb4f008d..6a0bc69fce2 100644 --- a/openbb_terminal/miscellaneous/i18n/en.yml +++ b/openbb_terminal/miscellaneous/i18n/en.yml @@ -792,11 +792,15 @@ en: fixedincome/ecb: key ECB interest rates fixedincome/_government_bonds_: Government Bonds fixedincome/treasury: plot countries (including forecast) treasury rates - fixedincome/usrates: plot secondary market, constant maturity or inflation protected rates - fixedincome/ycrv: plot US yield curve - fixedincome/ecbycrv: plot euro area yield curve + fixedincome/usrates: plot U.S. secondary market, constant maturity or inflation protected treasuries + fixedincome/ycrv: plot the yield curve based on constant maturity U.S. treasuries + fixedincome/ecbycrv: plot the yield curve based on Euro Area treasuries fixedincome/_corporate_bonds_: Corporate Bonds fixedincome/icebofa: display ICE BofA Corporate Indices + fixedincome/moody: display Moody's AAA and BAA Corporate indices + fixedincome/cp: display short-term, promissory notes issued by corporations + fixedincome/spot: display spot rates ranging from 1 year to a 100 years + fixedincome/hqm: display the High Quality Market (HQM) corporate yield curve fixedincome/_spreads_: Spreads fixedincome/icespread: display spread between ICE BofA Corporate Indices and spot treasury curve fixedincome/tmc: 10-year treasury constant maturity minus selected treasury constant maturity