Source code for BharatFinTrack.visual

import os
import typing
import pandas
import matplotlib.pyplot
import matplotlib.figure
from .helper import Helper
from .sip import SIP


[docs] class Visual: ''' Provides functionality for visualizing data. ''' def _validate_figure_file( self, figure_file: str ) -> None: ''' Validate the direction and extension of given figure file. ''' # Directory path and file name dir_path, file_name = os.path.split(figure_file) # Validate directory path if not os.path.isdir(dir_path): raise NotADirectoryError( f'Invalid directory path "{str(dir_path)}" for the figure file' ) # Create figure figure = matplotlib.pyplot.figure( figsize=(1, 1) ) # Check figure file extension fig_ext = os.path.splitext(file_name)[-1][1:] if fig_ext not in list(figure.canvas.get_supported_filetypes().keys()): raise TypeError( f'Input figure_file extension ".{fig_ext}" is not supported for saving the figure' ) matplotlib.pyplot.close(figure) return None
[docs] def cagr_leaders_by_category( self, excel_file: str, figure_file: str, number_leader: int = 5, fig_width: int | float = 15, fig_height: int | float = 10, fig_title: typing.Optional[str] = None, fig_ylabel: typing.Optional[str] = None, gui_window: bool = True ) -> matplotlib.figure.Figure: ''' Generate a bar plot of top-performing securities by CAGR (%) within each category since launch. Parameters ---------- excel_file : str Path to the input Excel file generated by :meth:`BharatFinTrack.CAGR.sort_since_inception` with ``within_category=True``. figure_file : str File Path to save the output figure. number_leader : int, optional Number of top indices to display per category based on CAGR (%). Default is 5. fig_width : float, optional Width of the figure in inches. Default is 15. fig_height : float, optional Height of the figure in inches. Default is 10. fig_title : str, optional Title of the figure. Default is None. fig_ylabel : str, optional Label for the y-axis.. Default is None. gui_window : bool, optional If True (default), open a graphical user interface window for the plot. Returns ------- Figure Bar plot showing the top CAGR (%) securities within each category. ''' # Check static type of input variable origin Helper()._validate_variable_origin_static_type( vars_types=typing.get_type_hints( obj=self.cagr_leaders_by_category ), vars_values=locals() ) # Check validity of figure file self._validate_figure_file( figure_file=figure_file ) # Input DataFrame df = pandas.read_excel( io=excel_file ) group_df = df.groupby(by='Category').head(number_leader) # Category of indices categories = group_df['Category'].unique() # Color for category colormap = matplotlib.colormaps.get_cmap('Set2') category_color = { categories[count]: colormap(count / len(categories)) for count in range(len(categories)) } # Create figure figure = matplotlib.pyplot.figure( figsize=(fig_width, fig_height) ) subplot = figure.subplots(1, 1) # Plotting indices CAGR(%) values categories_legend = set() for count, (index, row) in enumerate(group_df.iterrows()): category = row['Category'] color = category_color[category] if category not in categories_legend: subplot.barh( row['Index Name'], row['CAGR(%)'], color=color, label=category ) categories_legend.add(category) else: subplot.barh( row['Index Name'], row['CAGR(%)'], color=color ) subplot.text( row['CAGR(%)'], count, row['Y-M-D'], va='center', fontsize=8 ) # X-axis customization xtick_gap = 5 xaxis_max = int((group_df['CAGR(%)'].max() / xtick_gap) + 2) * xtick_gap subplot.set_xlim(0, xaxis_max) xticks = range(0, xaxis_max + 1, xtick_gap) subplot.set_xticks( ticks=xticks ) subplot.set_xticklabels( labels=[ str(val) for val in xticks ], fontsize=12 ) subplot.tick_params( axis='x', which='both', direction='in', length=6, width=1, top=True, bottom=True, labeltop=True, labelbottom=True ) subplot.grid( visible=True, which='major', axis='x', color='gray', linestyle='--', linewidth=0.3 ) close_date = group_df['Close Date'].iloc[0].strftime(Helper()._date_str_fmt) subplot.set_xlabel( f'CAGR (%) since launch (as of {close_date})', fontsize=12, labelpad=15 ) # Y-axis customization subplot.set_ylim(len(group_df), -1) subplot.tick_params( axis='y', labelsize=10 ) if fig_ylabel is not None: subplot.set_ylabel( fig_ylabel, fontsize=12, labelpad=15 ) # Legend subplot.legend( title='Category', loc='lower right', fontsize=12, title_fontsize=12 ) # Figure title if fig_title is not None: figure.suptitle( fig_title, fontsize=15, y=1 ) # Saving figure figure.tight_layout() figure.savefig( figure_file, bbox_inches='tight' ) # Close the figure to prevent duplicate plots from displaying matplotlib.pyplot.show() if gui_window else None matplotlib.pyplot.close(figure) return figure
[docs] def cagr_leaders( self, excel_file: str, figure_file: str, number_leader: int = 15, fig_width: int | float = 15, fig_height: int | float = 10, fig_title: typing.Optional[str] = None, fig_ylabel: typing.Optional[str] = None, gui_window: bool = True ) -> matplotlib.figure.Figure: ''' Generate a bar plot of top-performing securities by CAGR (%) since launch. Parameters ---------- excel_file : str Path to the input Excel file generated by :meth:`BharatFinTrack.CAGR.sort_since_inception` with ``within_category=False``. figure_file : str File Path to save the output figure. number_leader : int, optional Number of top indices to display based on CAGR (%). Default is 15. fig_width : float, optional Width of the figure in inches. Default is 15. fig_height : float, optional Height of the figure in inches. Default is 10. fig_title : str, optional Title of the figure. Default is None. fig_ylabel : str, optional Label for the y-axis.. Default is None. gui_window : bool, optional If True (default), open a graphical user interface window for the plot. Returns ------- Figure Bar plot showing the top CAGR (%) securities. ''' # Check static type of input variable origin Helper()._validate_variable_origin_static_type( vars_types=typing.get_type_hints( obj=self.cagr_leaders ), vars_values=locals() ) # Check validity of figure file self._validate_figure_file( figure_file=figure_file ) # Input DataFrame df = pandas.read_excel( io=excel_file ).head(number_leader) # Create figure figure = matplotlib.pyplot.figure( figsize=(fig_width, fig_height) ) subplot = figure.subplots(1, 1) # Plotting indices CAGR(%) values for count, (index, row) in enumerate(df.iterrows()): subplot.barh( row['Index Name'], row['CAGR(%)'], color='lawngreen' ) subplot.text( row['CAGR(%)'], count, row['Y-M-D'], va='center', fontsize=8 ) # X-axis customization xtick_gap = 5 xaxis_max = int((df['CAGR(%)'].max() / xtick_gap) + 2) * xtick_gap subplot.set_xlim(0, xaxis_max) xticks = range(0, xaxis_max + 1, xtick_gap) subplot.set_xticks( ticks=xticks ) subplot.set_xticklabels( labels=[ str(val) for val in xticks ], fontsize=12 ) subplot.tick_params( axis='x', which='both', direction='in', length=6, width=1, top=True, bottom=True, labeltop=True, labelbottom=True ) subplot.grid( visible=True, which='major', axis='x', color='gray', linestyle='--', linewidth=0.3 ) close_date = df['Close Date'].iloc[0].strftime(Helper()._date_str_fmt) subplot.set_xlabel( f'CAGR (%) since launch (as of {close_date})', fontsize=12, labelpad=15 ) # Y-axis customization subplot.set_ylim(len(df), -1) subplot.tick_params( axis='y', labelsize=10 ) if fig_ylabel is not None: subplot.set_ylabel( fig_ylabel, fontsize=12, labelpad=15 ) # Figure title if fig_title is not None: figure.suptitle( fig_title, fontsize=15, y=1 ) # Saving figure figure.tight_layout() figure.savefig( figure_file, bbox_inches='tight' ) # Close the figure to prevent duplicate plots from displaying matplotlib.pyplot.show() if gui_window else None matplotlib.pyplot.close(figure) return figure
[docs] def compare_sip_to_bond_benchmark( self, excel_file: str, figure_file: str, bond_yield: int | float = 7, fig_width: int | float = 15, fig_height: int | float = 10, fig_title: typing.Optional[str] = None, ytick_gap: int = 200, gui_window: bool = True, asset_legend: str = 'Index growth' ) -> matplotlib.figure.Figure: ''' Generate a bar plot comparing the SIP value of a specified security against government bond over time, assuming a monthly investment of 1 Rupee. Parameters ---------- excel_file : str Path to the input Excel file generated by :meth:`BharatFinTrack.SIP.yearly_return`. figure_file : str File Path to save the output figure. bond_yield : float, optional Expected annual yield (%) of government securities. Default is 7. fig_width : float, optional Width of the figure in inches. Default is 15. fig_height : float, optional Height of the figure in inches. Default is 10. fig_title : str, optional Title of the figure. Default is None. ytick_gap : int, optional Gap between two y-axis ticks. Default is 200. gui_window : bool, optional If True (default), open a graphical user interface window for the plot. asset_legend : str, optional Label used for the asset in the plot legend. Default is 'Index growth'. Returns ------- Figure Bar plot comparing SIP values of the security and government bond over time. ''' # Check static type of input variable origin Helper()._validate_variable_origin_static_type( vars_types=typing.get_type_hints( obj=self.compare_sip_to_bond_benchmark ), vars_values=locals() ) # Check validity of figure file self._validate_figure_file( figure_file=figure_file ) # SIP DataFRame df = pandas.read_excel( io=excel_file ) sip_years = int(df['Year'].max()) df = df[df['Year'] <= sip_years].reset_index(drop=True) monthly_sip = df['Investment'].iloc[0] / 12 df['Investment'] = df['Investment'] / monthly_sip df['Close Value'] = df['Close Value'] / monthly_sip # Government security growth DataFrame gsec_df = SIP().investment_growth( invest=1, frequency='monthly', annual_return=bond_yield, years=sip_years ) # Create figure figure = matplotlib.pyplot.figure( figsize=(fig_width, fig_height) ) subplot = figure.subplots(1, 1) # Plot SIP growth value xticks = pandas.Series( data=range(len(df)) ) bar_width = 0.3 subplot.bar( x=xticks - bar_width, height=df['Investment'], width=bar_width, label='Investment', color='gold' ) subplot.bar( x=xticks, height=gsec_df['Close Value'], width=bar_width, label='G-Sec growth', color='cyan' ) subplot.bar( x=xticks + bar_width, height=df['Close Value'], width=bar_width, label=asset_legend, color='lightgreen' ) for xt in xticks: multiple = df['Multiple(X)'].iloc[xt] xirr = df['XIRR(%)'].iloc[xt] subplot.annotate( f'{multiple:.1f}X\n{xirr:.1f}%', xy=(xticks.iloc[xt] + bar_width, df['Close Value'].iloc[xt]), ha='center', va='bottom', fontsize=8 ) # X-axis customization subplot.set_xticks( ticks=xticks ) xticklabels = list( map( lambda x: x[0].strftime(Helper()._date_str_fmt) + f' ({x[1]:.0f}Y)', zip(df['Start Date'], df['Year']) ) ) subplot.set_xticklabels( labels=xticklabels, rotation=90, fontsize=10 ) subplot.set_xlabel( xlabel='Start Date', fontsize=12 ) # Y-axis customization df_max = df['Close Value'].max() gsec_max = gsec_df['Close Value'].max() yaxis_max = (round(max(df_max, gsec_max) / ytick_gap) + 2) * ytick_gap subplot.set_ylim(0, yaxis_max) yticks = range(0, yaxis_max + 1, ytick_gap) subplot.set_yticks( ticks=yticks ) subplot.set_yticklabels( labels=[ str(yt) for yt in yticks ], fontsize=10 ) subplot.tick_params( axis='y', which='both', direction='in', length=6, width=1, left=True, right=True, labelleft=True ) subplot.grid( visible=True, which='major', axis='y', color='gray', linestyle='--', linewidth=0.3 ) close_date = df['Close Date'].iloc[0].strftime(Helper()._date_str_fmt) subplot.set_ylabel( ylabel=f'Amount (as of {close_date})', fontsize=12 ) # Legend subplot.legend( loc='upper left', fontsize=12 ) # Figure title if fig_title is not None: figure.suptitle( fig_title, fontsize=15, y=1 ) # Saving figure figure.tight_layout() figure.savefig( figure_file, bbox_inches='tight' ) # Close the figure to prevent duplicate plots from displaying matplotlib.pyplot.show() if gui_window else None matplotlib.pyplot.close(figure) return figure
[docs] def compare_performance( self, excel_file: str, figure_file: str, fig_width: int | float = 15, fig_height: int | float = 10, fig_title: typing.Optional[str] = None, ytick_gap: int = 2, gui_window: bool = True ) -> matplotlib.figure.Figure: ''' Generate a line plot comparing growth multiple of investment across multiple securities over the years. Parameters ---------- excel_file : str Path to the input Excel file generated by either :meth:`BharatFinTrack.SIP.compare_performance` or :meth:`BharatFinTrack.CAGR.compare_performance`. figure_file : str File Path to save the output figure. fig_width : float, optional Width of the figure in inches. Default is 15. fig_height : float, optional Height of the figure in inches. Default is 10. fig_title : str, optional Title of the figure. Default is None. ytick_gap : int, optional Gap between two y-axis ticks. Default is 200. gui_window : bool, optional If True (default), open a graphical user interface window for the plot. Returns ------- Figure Line plot comparing growth multiple of investment across multiple securities over the years. ''' # Check static type of input variable origin Helper()._validate_variable_origin_static_type( vars_types=typing.get_type_hints( obj=self.compare_performance ), vars_values=locals() ) # Check validity of figure file self._validate_figure_file( figure_file=figure_file ) # Compare DataFRame df = pandas.read_excel( io=excel_file, sheet_name='Growth(X)' ) # Number of assets com_cols = 3 number_asset = df.shape[1] - com_cols # Color for assets colormap = matplotlib.colormaps.get_cmap('hsv') colors = [ colormap(count / number_asset) for count in range(number_asset) ] # Create figure figure = matplotlib.pyplot.figure( figsize=(fig_width, fig_height) ) subplot = figure.subplots(1, 1) # Plot SIP growth multiple for index, color in zip(df.columns[com_cols:], colors): subplot.plot( df.index, df[index], marker='o', markersize=8, color=color, label=index ) # X-axis customization xticks = pandas.Series( data=range(len(df)) ) subplot.set_xticks( ticks=xticks ) xticklabels = list( map( lambda x: x[0].strftime(Helper()._date_str_fmt) + f' ({x[1]:.0f}Y)', zip(df['Start Date'], df['Year']) ) ) subplot.set_xticklabels( labels=xticklabels, rotation=90, fontsize=10 ) subplot.set_xlabel( xlabel='Start Date', fontsize=12 ) # Y-axis customization growth_max = df.iloc[:, com_cols:].max().max() yaxis_max = (round(growth_max / ytick_gap) + 2) * ytick_gap subplot.set_ylim(0, yaxis_max) yticks = range(0, yaxis_max + 1, ytick_gap) subplot.set_yticks( ticks=yticks ) subplot.set_yticklabels( labels=[ str(yt) for yt in yticks ], fontsize=10 ) subplot.tick_params( axis='y', which='both', direction='in', length=6, width=1, left=True, right=True, labelleft=True, labelright=True ) subplot.grid( visible=True, which='major', axis='y', color='gray', linestyle='--', linewidth=0.3 ) close_date = df['Close Date'].iloc[0].strftime(Helper()._date_str_fmt) subplot.set_ylabel( ylabel=f'Multiples (X) of Investment (as of {close_date})', fontsize=12 ) # Legend subplot.legend( loc='upper left', fontsize=12 ) # Figure title if fig_title is not None: figure.suptitle( fig_title, fontsize=15, y=1 ) # Saving figure figure.tight_layout() figure.savefig( figure_file, bbox_inches='tight' ) # Close the figure to prevent duplicate plots from displaying matplotlib.pyplot.show() if gui_window else None matplotlib.pyplot.close(figure) return figure