diff --git a/BharatFinTrack/__init__.py b/BharatFinTrack/__init__.py index 4986bbf..29c5764 100644 --- a/BharatFinTrack/__init__.py +++ b/BharatFinTrack/__init__.py @@ -10,4 +10,4 @@ ] -__version__ = '0.1.2' +__version__ = '0.1.3' diff --git a/BharatFinTrack/core.py b/BharatFinTrack/core.py index 6fd7e08..b83806d 100644 --- a/BharatFinTrack/core.py +++ b/BharatFinTrack/core.py @@ -125,7 +125,8 @@ def _download_nse_tri( response = requests.post( url=self.url_nse_index_tri_data, headers=headers, - data=payload + data=payload, + timeout=30 ) response_data = response.json() records = json.loads(response_data['d']) diff --git a/BharatFinTrack/data/equity_indices.xlsx b/BharatFinTrack/data/equity_indices.xlsx index 99ebf97..c9467f5 100644 Binary files a/BharatFinTrack/data/equity_indices.xlsx and b/BharatFinTrack/data/equity_indices.xlsx differ diff --git a/BharatFinTrack/nse_index.py b/BharatFinTrack/nse_index.py index c7ee03d..12ca375 100644 --- a/BharatFinTrack/nse_index.py +++ b/BharatFinTrack/nse_index.py @@ -7,7 +7,6 @@ import requests import bs4 import matplotlib -import warnings from .nse_product import NSEProduct from .core import Core @@ -19,29 +18,29 @@ class NSEIndex: (excluding dividend reinvestment). ''' - def equity_cagr_from_launch( + def download_daily_summary_report( self, - http_headers: typing.Optional[dict[str, str]] = None, - untracked_indices: bool = False + folder_path: str, + http_headers: typing.Optional[dict[str, str]] = None ) -> pandas.DataFrame: ''' - Returns a DataFrame with the CAGR(%) of all NSE equity indices from inception. + Download the daily summary for all NSE indices and save it as the + 'daily_summary_report.csv' file in the specified folder path. Parameters ---------- + folder_path: str + Folder path to save the CSV file of the daily summary for all NSE indices. + http_headers : dict, optional HTTP headers for the web request. Defaults to :attr:`BharatFinTrack.core.Core.default_http_headers` if not provided. - untracked_indices : bool, optional - Defaults to False. If True, print two lists of untracked indices - from downloaded and based files. - Returns ------- DataFrame - A DataFrame with the CAGR(%) for all NSE equity indices from inception. + A DataFrame containing the daily summary for all NSE indices. ''' # web request headers @@ -66,14 +65,47 @@ def equity_cagr_from_launch( url=csv_link, headers=headers ) - with tempfile.TemporaryDirectory() as tmp_dir: - download_file = os.path.join(tmp_dir, 'daily.csv') + if os.path.isdir(folder_path): + download_file = os.path.join(folder_path, 'daily_summary_report.csv') with open(download_file, 'wb') as download_data: download_data.write(response.content) - download_df = pandas.read_csv(download_file) + output = pandas.read_csv(download_file) + else: + raise Exception('The folder path does not exist.') else: pass + return output + + def equity_cagr_from_launch( + self, + http_headers: typing.Optional[dict[str, str]] = None, + untracked_indices: bool = False + ) -> pandas.DataFrame: + + ''' + Returns the CAGR (%) since launch for all NSE equity indices. + + Parameters + ---------- + http_headers : dict, optional + HTTP headers for the web request. Defaults to + :attr:`BharatFinTrack.core.Core.default_http_headers` if not provided. + + untracked_indices : bool, optional + Defaults to False. If True, print two lists of untracked indices + from downloaded and based files. + + Returns + ------- + DataFrame + A DataFrame with the CAGR (%) since launch for all NSE equity indices. + ''' + + # downlod daily summary of NSE indices + with tempfile.TemporaryDirectory() as tmp_dir: + download_df = self.download_daily_summary_report(tmp_dir) + # processing downloaded data date_string = datetime.datetime.strptime( download_df.loc[0, 'Index Date'], '%d-%m-%Y' @@ -82,9 +114,9 @@ def equity_cagr_from_launch( download_df = download_df[ ['Index Name', 'Index Date', 'Closing Index Value'] ] - download_df.columns = ['Index Name', 'Date', 'Close'] + download_df.columns = ['Index Name', 'Close Date', 'Close Value'] download_df['Index Name'] = download_df['Index Name'].apply(lambda x: x.upper()) - download_df['Date'] = download_date + download_df['Close Date'] = download_date # processing base DataFrame base_df = NSEProduct()._dataframe_equity_index @@ -104,7 +136,7 @@ def equity_cagr_from_launch( # computing CAGR(%) cagr_df = base_df.merge(download_df) - cagr_df['Close/Base Value'] = cagr_df['Close'] / cagr_df['Base Value'] + cagr_df['Close/Base'] = cagr_df['Close Value'] / cagr_df['Base Value'] cagr_df['Years'] = list( map( lambda x: dateutil.relativedelta.relativedelta(download_date, x).years, cagr_df['Base Date'] @@ -116,7 +148,7 @@ def equity_cagr_from_launch( ) ) total_years = cagr_df['Years'] + (cagr_df['Days'] / 365) - cagr_df['CAGR(%)'] = 100 * (pow(cagr_df['Close'] / cagr_df['Base Value'], 1 / total_years) - 1) + cagr_df['CAGR(%)'] = 100 * (pow(cagr_df['Close Value'] / cagr_df['Base Value'], 1 / total_years) - 1) # output if untracked_indices is False: @@ -138,8 +170,7 @@ def sort_equity_cagr_from_launch( ) -> pandas.DataFrame: ''' - Returns a DataFrame where equity indices are sorted - in descending order of CAGR(%). + Returns equity indices sorted in descending order by CAGR (%) since launch. Parameters ---------- @@ -153,8 +184,7 @@ def sort_equity_cagr_from_launch( Returns ------- DataFrame - A DataFrame with the CAGR(%) for all NSE equity indices from inception, - sorted in descending order by CAGR(%). + A DataFrame sorted in descending order by CAGR (%) since launch for all NSE equity indices. ''' # DataFrame of CAGR(%) @@ -171,7 +201,7 @@ def sort_equity_cagr_from_launch( ) output = cagr_df.reset_index(drop=True) - # saving the DataFrame + # saving DataFrame excel_ext = Core()._excel_file_extension(excel_file) if excel_ext != '.xlsx': raise Exception( @@ -183,15 +213,20 @@ def sort_equity_cagr_from_launch( workbook = excel_writer.book worksheet = excel_writer.sheets['Sheet1'] # format columns - for col_num, df_col in enumerate(output.columns): - if df_col == 'Index Name': + for col_num, col_df in enumerate(output.columns): + if col_df == 'Index Name': worksheet.set_column(col_num, col_num, 60) - elif df_col == 'Close/Base Value': + elif col_df == 'Close Value': + worksheet.set_column( + col_num, col_num, 15, + workbook.add_format({'num_format': '#,##0'}) + ) + elif col_df == 'Close/Base': worksheet.set_column( col_num, col_num, 15, workbook.add_format({'num_format': '#,##0.0'}) ) - elif df_col == 'CAGR(%)': + elif col_df == 'CAGR(%)': worksheet.set_column( col_num, col_num, 15, workbook.add_format({'num_format': '#,##0.00'}) @@ -208,8 +243,8 @@ def category_sort_equity_cagr_from_launch( ) -> pandas.DataFrame: ''' - Returns a multi-index DataFrame where equity indices are sorted - in descending order of CAGR(%) from inception within each category. + Returns equity indices sorted in descending order by CAGR (%) + since launch within each index category. Parameters ---------- @@ -223,8 +258,8 @@ def category_sort_equity_cagr_from_launch( Returns ------- DataFrame - A multi-index DataFrame with the CAGR(%) for all NSE equity indices from inception, - sorted in descending order by CAGR(%) within each index category. + A multi-index DataFrame sorted in descending order by CAGR (%) + since launch within each index category. ''' # DataFrame of CAGR(%) @@ -240,13 +275,11 @@ def category_sort_equity_cagr_from_launch( ordered=True ) - # Sort the dataframe + # Sorting Dataframe cagr_df = cagr_df.sort_values( by=['Category', 'CAGR(%)', 'Years', 'Days'], ascending=[True, False, False, False] ) - - # output dataframes = [] for category in categories: category_df = cagr_df[cagr_df['Category'] == category] @@ -273,15 +306,20 @@ def category_sort_equity_cagr_from_launch( index_cols = len(output.index.names) # format columns worksheet.set_column(0, index_cols - 1, 15) - for col_num, df_col in enumerate(output.columns): - if df_col == 'Index Name': + for col_num, col_df in enumerate(output.columns): + if col_df == 'Index Name': worksheet.set_column(index_cols + col_num, index_cols + col_num, 60) - elif df_col == 'Close/Base Value': + elif col_df == 'Close Value': + worksheet.set_column( + index_cols + col_num, index_cols + col_num, 15, + workbook.add_format({'num_format': '#,##0'}) + ) + elif col_df == 'Close/Base': worksheet.set_column( index_cols + col_num, index_cols + col_num, 15, workbook.add_format({'num_format': '#,##0.0'}) ) - elif df_col == 'CAGR(%)': + elif col_df == 'CAGR(%)': worksheet.set_column( index_cols + col_num, index_cols + col_num, 15, workbook.add_format({'num_format': '#,##0.00'}) @@ -310,32 +348,3 @@ def category_sort_equity_cagr_from_launch( start_row = end_row + 1 return output - - def all_equity_index_cagr_from_inception( - self, - excel_file: str, - http_headers: typing.Optional[dict[str, str]] = None - ) -> pandas.DataFrame: - - ''' - .. warning:: - :meth:`NSEIndex.all_equity_index_cagr_from_inception` is deprecated and will be removed in version 0.1.3. - Use :meth:`NSEIndex.category_sort_equity_cagr_from_launch` instead. - ''' - - message = '''Use the method category_sort_equity_cagr_from_launch(excel_file) - instead of the deprecated method all_equity_index_cagr_from_inception(excel_file), - which will be removed in version 0.1.3. - ''' - warnings.warn( - message, - DeprecationWarning, - stacklevel=2 - ) - - output = self.category_sort_equity_cagr_from_launch( - excel_file=excel_file, - http_headers=http_headers - ) - - return output diff --git a/BharatFinTrack/nse_tri.py b/BharatFinTrack/nse_tri.py index 1eff518..8f09c1e 100644 --- a/BharatFinTrack/nse_tri.py +++ b/BharatFinTrack/nse_tri.py @@ -1,6 +1,8 @@ import typing import datetime +import dateutil.relativedelta import pandas +import matplotlib from .nse_product import NSEProduct from .core import Core @@ -83,7 +85,7 @@ def download_historical_daily_data( ''' Downloads historical daily closing values for the specified index - between the given start and end dates, both inclusive, and returns them in a DataFrame. + between the given start and end dates, both inclusive. Parameters ---------- @@ -165,3 +167,339 @@ def download_historical_daily_data( worksheet.set_column(0, 1, 12) return df + + def download_equity_indices_updated_value( + self, + excel_file: str, + http_headers: typing.Optional[dict[str, str]] = None + ) -> pandas.DataFrame: + + ''' + Returns updated TRI values for all NSE indices. + + Parameters + ---------- + excel_file : str, optional + Path to an Excel file to save the DataFrame. + + http_headers : dict, optional + HTTP headers for the web request. Defaults to + :attr:`BharatFinTrack.core.Core.default_http_headers` if not provided. + + Returns + ------- + DataFrame + A DataFrame containing updated TRI values for all NSE indices. + ''' + + # processing base DataFrame + base_df = NSEProduct()._dataframe_equity_index + base_df = base_df.reset_index() + base_df = base_df[base_df['API TRI'] != 'NON OPEN SOURCE'].reset_index(drop=True) + base_df = base_df.drop(columns=['ID', 'API TRI']) + base_df['Base Date'] = base_df['Base Date'].apply(lambda x: x.date()) + + # check the Excel file extension first + excel_ext = Core()._excel_file_extension(excel_file) + if excel_ext == '.xlsx': + pass + else: + raise Exception(f'Input file extension "{excel_ext}" does not match the required ".xlsx".') + + # downloading data + today = datetime.date.today() + week_ago = today - datetime.timedelta(days=7) + end_date = today.strftime('%d-%b-%Y') + start_date = week_ago.strftime('%d-%b-%Y') + for base_index in base_df.index: + index_df = self.download_historical_daily_data( + index=base_df.loc[base_index, 'Index Name'], + start_date=start_date, + end_date=end_date + ) + base_df.loc[base_index, 'Close Date'] = index_df.iloc[-1, 0] + base_df.loc[base_index, 'Close Value'] = index_df.iloc[-1, -1] + + # saving the DataFrame + with pandas.ExcelWriter(excel_file, engine='xlsxwriter') as excel_writer: + base_df.to_excel(excel_writer, index=False) + worksheet = excel_writer.sheets['Sheet1'] + # format columns + for col_num, df_col in enumerate(base_df.columns): + if df_col == 'Index Name': + worksheet.set_column(col_num, col_num, 60) + else: + worksheet.set_column(col_num, col_num, 15) + + return base_df + + def sort_equity_value_from_launch( + self, + input_excel: str, + output_excel: str, + ) -> pandas.DataFrame: + + ''' + Returns equity indices sorted in descending order by TRI values since launch. + + Parameters + ---------- + inout_excel : str + Path to the input Excel file. + + output_excel : str + Path to an output Excel file to save the output DataFrame. + + Returns + ------- + DataFrame + A DataFrame sorted in descending order by TRI values since launch. + ''' + + # sorting DataFrame by TRI values + df = pandas.read_excel(input_excel) + df = df.drop(columns=['Category']) + df = df.sort_values( + by=['Close Value'], + ascending=[False] + ) + df = df.reset_index(drop=True) + for col_df in df.columns: + if 'Date' in col_df: + df[col_df] = df[col_df].apply(lambda x: x.date()) + else: + pass + + # saving the DataFrame + excel_ext = Core()._excel_file_extension(output_excel) + if excel_ext != '.xlsx': + raise Exception( + f'Input file extension "{excel_ext}" does not match the required ".xlsx".' + ) + else: + with pandas.ExcelWriter(output_excel, engine='xlsxwriter') as excel_writer: + df.to_excel(excel_writer, index=False) + worksheet = excel_writer.sheets['Sheet1'] + # format columns + for col_num, col_df in enumerate(df.columns): + if col_df == 'Index Name': + worksheet.set_column(col_num, col_num, 60) + else: + worksheet.set_column(col_num, col_num, 15) + + return df + + def sort_equity_cagr_from_launch( + self, + input_excel: str, + output_excel: str, + ) -> pandas.DataFrame: + + ''' + Returns equity indices sorted in descending order by CAGR (%) since launch. + + Parameters + ---------- + inout_excel : str + Path to the input Excel file. + + output_excel : str + Path to an output Excel file to save the output DataFrame. + + Returns + ------- + DataFrame + A DataFrame sorted in descending order by CAGR (%) values since launch. + ''' + + # DataFrame processing + df = pandas.read_excel(input_excel) + df = df.drop(columns=['Category']) + for col_df in df.columns: + if 'Date' in col_df: + df[col_df] = df[col_df].apply(lambda x: x.date()) + else: + pass + df['Close/Base'] = df['Close Value'] / df['Base Value'] + df['Years'] = list( + map( + lambda x, y: dateutil.relativedelta.relativedelta(x, y).years, df['Close Date'], df['Base Date'] + ) + ) + df['Days'] = list( + map( + lambda x, y, z: (x - y.replace(year=y.year + z)).days, df['Close Date'], df['Base Date'], df['Years'] + ) + ) + total_years = df['Years'] + (df['Days'] / 365) + df['CAGR(%)'] = 100 * (pow(df['Close Value'] / df['Base Value'], 1 / total_years) - 1) + + # sorting DataFrame by CAGR (%) values + df = df.sort_values( + by=['CAGR(%)', 'Years', 'Days'], + ascending=[False, False, False] + ) + df = df.reset_index(drop=True) + + # saving the DataFrame + excel_ext = Core()._excel_file_extension(output_excel) + if excel_ext != '.xlsx': + raise Exception( + f'Input file extension "{excel_ext}" does not match the required ".xlsx".' + ) + else: + with pandas.ExcelWriter(output_excel, engine='xlsxwriter') as excel_writer: + df.to_excel(excel_writer, index=False) + workbook = excel_writer.book + worksheet = excel_writer.sheets['Sheet1'] + # format columns + for col_num, col_df in enumerate(df.columns): + if col_df == 'Index Name': + worksheet.set_column(col_num, col_num, 60) + elif col_df == 'Close Value': + worksheet.set_column( + col_num, col_num, 15, + workbook.add_format({'num_format': '#,##0'}) + ) + elif col_df == 'Close/Base': + worksheet.set_column( + col_num, col_num, 15, + workbook.add_format({'num_format': '#,##0.0'}) + ) + elif col_df == 'CAGR(%)': + worksheet.set_column( + col_num, col_num, 15, + workbook.add_format({'num_format': '#,##0.00'}) + ) + else: + worksheet.set_column(col_num, col_num, 15) + + return df + + def category_sort_equity_cagr_from_launch( + self, + input_excel: str, + output_excel: str, + ) -> pandas.DataFrame: + + ''' + Returns equity indices sorted in descending order by CAGR (%) since launch + within each index category. + + Parameters + ---------- + inout_excel : str + Path to the input Excel file. + + output_excel : str + Path to an output Excel file to save the output DataFrame. + + Returns + ------- + DataFrame + A multi-index DataFrame sorted in descending order by CAGR (%) values since launch + within each index category. + ''' + + # DataFrame processing + df = pandas.read_excel(input_excel) + for col_df in df.columns: + if 'Date' in col_df: + df[col_df] = df[col_df].apply(lambda x: x.date()) + else: + pass + df['Close/Base'] = df['Close Value'] / df['Base Value'] + df['Years'] = list( + map( + lambda x, y: dateutil.relativedelta.relativedelta(x, y).years, df['Close Date'], df['Base Date'] + ) + ) + df['Days'] = list( + map( + lambda x, y, z: (x - y.replace(year=y.year + z)).days, df['Close Date'], df['Base Date'], df['Years'] + ) + ) + total_years = df['Years'] + (df['Days'] / 365) + df['CAGR(%)'] = 100 * (pow(df['Close Value'] / df['Base Value'], 1 / total_years) - 1) + + # Convert 'Category' column to categorical data types with a defined order + categories = list(df['Category'].unique()) + df['Category'] = pandas.Categorical( + df['Category'], + categories=categories, + ordered=True + ) + + # Sorting Dataframe + df = df.sort_values( + by=['Category', 'CAGR(%)', 'Years', 'Days'], + ascending=[True, False, False, False] + ) + dataframes = [] + for category in categories: + category_df = df[df['Category'] == category] + category_df = category_df.drop(columns=['Category']).reset_index(drop=True) + dataframes.append(category_df) + output = pandas.concat( + dataframes, + keys=[word.upper() for word in categories], + names=['Category', 'ID'] + ) + + # saving the DataFrame + excel_ext = Core()._excel_file_extension(output_excel) + if excel_ext != '.xlsx': + raise Exception( + f'Input file extension "{excel_ext}" does not match the required ".xlsx".' + ) + else: + with pandas.ExcelWriter(output_excel, engine='xlsxwriter') as excel_writer: + output.to_excel(excel_writer, index=True) + workbook = excel_writer.book + worksheet = excel_writer.sheets['Sheet1'] + # number of columns for DataFrame indices + index_cols = len(output.index.names) + # format columns + worksheet.set_column(0, index_cols - 1, 15) + for col_num, col_df in enumerate(output.columns): + if col_df == 'Index Name': + worksheet.set_column(index_cols + col_num, index_cols + col_num, 60) + elif col_df == 'Close Value': + worksheet.set_column( + index_cols + col_num, index_cols + col_num, 15, + workbook.add_format({'num_format': '#,##0'}) + ) + elif col_df == 'Close/Base': + worksheet.set_column( + index_cols + col_num, index_cols + col_num, 15, + workbook.add_format({'num_format': '#,##0.0'}) + ) + elif col_df == 'CAGR(%)': + worksheet.set_column( + index_cols + col_num, index_cols + col_num, 15, + workbook.add_format({'num_format': '#,##0.00'}) + ) + else: + worksheet.set_column(index_cols + col_num, index_cols + col_num, 15) + # Dataframe colors + get_colormap = matplotlib.colormaps.get_cmap('Pastel2') + colors = [ + get_colormap(count / len(dataframes)) for count in range(len(dataframes)) + ] + hex_colors = [ + '{:02X}{:02X}{:02X}'.format(*[int(num * 255) for num in color]) for color in colors + ] + # coloring of DataFrames + start_col = index_cols - 1 + end_col = index_cols + len(output.columns) - 1 + start_row = 1 + for df, color in zip(dataframes, hex_colors): + color_format = workbook.add_format({'bg_color': color}) + end_row = start_row + len(df) - 1 + worksheet.conditional_format( + start_row, start_col, end_row, end_col, + {'type': 'no_blanks', 'format': color_format} + ) + start_row = end_row + 1 + + return output diff --git a/docs/changelog.rst b/docs/changelog.rst index f6bf38c..a3e165a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -3,6 +3,19 @@ Release Notes =============== +Version 0.1.3 +-------------- + +* **Release date:** 06-Oct-2024 + +* **Feature Additions:** + + * Fetches updated Total Return Index values for all NSE equity indices. + * Sorts the CAGR (%) of all NSE equity TRI values since launch. + +* **Bug Fixes:** Issues with the API used to fetch Total Return Index data. + + Version 0.1.2 -------------- @@ -11,8 +24,9 @@ Version 0.1.2 * **Changes:** * Deprecated :meth:`BharatFinTrack.NSEIndex.all_equity_index_cagr_from_inception` and introduced :meth:`BharatFinTrack.NSEIndex.category_sort_equity_cagr_from_launch`. - * Added functionality for sorting the CAGR (%) of all NSE equity indices from launch. - * Introduced sorting of the CAGR (%) of NSE equity indices from launch while maintaining fixed index categories. + +* **Feature Additions:** Added functionality for sorting the CAGR (%) of all NSE equity indices since launch. + * **Development Status:** Upgraded from Alpha to Beta. @@ -35,7 +49,7 @@ Version 0.1.0 * **Release date:** 30-Sep-2024. -* **Feature Additions:** Introduced :class:`BharatFinTrack.NSETRI` class, which facilitates downloading Total Return Index (TRI) data for all NSE equity indices. +* **Feature Additions:** Introduced :class:`BharatFinTrack.NSETRI` class, which facilitates downloading Total Return Index data for all NSE equity indices. * **Changes:** diff --git a/docs/functionality.rst b/docs/functionality.rst index fe7a359..ab148e7 100644 --- a/docs/functionality.rst +++ b/docs/functionality.rst @@ -14,8 +14,6 @@ This feature helps users make informed decisions about investments in passive fu .. code-block:: python - import BharatFinTrack - nse_index = BharatFinTrack.NSEIndex() nse_index.sort_equity_cagr_from_launch( excel_file=r"C:\Users\Username\Folder\sort_cagr.xlsx" ) @@ -30,6 +28,39 @@ better understand the difference in index returns across various categories. nse_index.category_sort_equity_cagr_from_launch( excel_file=r"C:\Users\Username\Folder\category_sort_cagr.xlsx" ) + + +Equity Total Return Index (TRI) Summary +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Download the updated TRI values for all NSE indices. These values are not updated on the website on a daily basis. +It is recommended to use this function at night when web traffic to the website is lower. The function sends several web requests to collect the required values. + +.. code-block:: python + + excel_file = r"C:\Users\Username\Folder\tri_updated_value.xlsx" + + # updated TRI value + nse_tri.download_equity_indices_updated_value( + excel_file=excel_file + ) + + # sort equity indices by updated value since launch + nse_tri.sort_equity_value_from_launch( + input_excel=excel_file, + output_excel=r"C:\Users\Username\Folder\sorted_tri_value.xlsx" + ) + + # sort equity indices by CAGR (%) since launch + nse_tri.sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel=r"C:\Users\Username\Folder\sorted_tri_cagr.xlsx" + ) + + # sort equity indices by CAGR (%) since launch within each category + nse_tri.category_sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel=r"C:\Users\Username\Folder\category_sort_tri_cagr.xlsx" + ) diff --git a/docs/introduction.rst b/docs/introduction.rst index ad0eee4..c145785 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -8,6 +8,12 @@ BharatFinTrack is a Python package designed to simplify the process of downloadi * `Nifty Indices `_ - Provides access to the characteristics of NSE equity indices. - - Calculates the CAGR(%) of all NSE equity indices (excluding dividend reinvestment) from inception. - - Facilitates downloading Total Return Index, including both price and dividend reinvestment, data for all NSE equity indices. + - Fetches updated values of prices (excluding dividend reinvestment) and Total Return Index (TRI) for all NSE equity indices. + - Facilitates downloading TRI data for all NSE equity indices between the specified start and end dates, inclusive. + + +* Analysis + + - Calculates the updated CAGR (%) of all NSE equity index prices and TRI since their inception. + - Sorts equity indices by CAGR (%) values since inception. \ No newline at end of file diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 622cf1e..331093d 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -13,6 +13,8 @@ Ensure successful installation by running the following commands: import BharatFinTrack nse_product = BharatFinTrack.NSEProduct() + nse_index = BharatFinTrack.NSEIndex() + nse_tri = BharatFinTrack.NSETRI() NSE Equity Index Characteristics @@ -36,7 +38,7 @@ Expected output: Index List -^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^ Get the list of all NSE equity indices: @@ -97,28 +99,27 @@ Expected output: Download Data --------------- +NSE Indices Summary +^^^^^^^^^^^^^^^^^^^^^^^^^^ +Download the daily summary report for all NSE indices, which is uploaded daily on the `Nifty Indices Reports `_, and save +as 'daily_summary_report.csv' in the specified folder path. + +.. code-block:: python + + nse_index.download_daily_summary_report( + folder_path=r"C:\Users\Username\Folder" + ) + + Total Return Index (TRI) ^^^^^^^^^^^^^^^^^^^^^^^^^^ -Download historical daily TRI data, including both price and dividend reinvestment, for the NIFTY 50 index: +Download historical daily TRI data, including both price and dividend reinvestment, for the NIFTY 50 index. +Currently, the function supports only equity indices. .. code-block:: python - import BharatFinTrack - nse_tri = BharatFinTrack.NSETRI() nse_tri.download_historical_daily_data( index='NIFTY 50', start_date='23-Sep-2024', end_date='27-Sep-2024' ) - - -Expected output: - -.. code-block:: text - - Date Close - 0 2024-09-23 38505.51 - 1 2024-09-24 38507.55 - 2 2024-09-25 38602.21 - 3 2024-09-26 38916.76 - 4 2024-09-27 38861.64 \ No newline at end of file diff --git a/tests/test_bharatfintrack.py b/tests/test_bharatfintrack.py index 4369f94..6e7f13e 100644 --- a/tests/test_bharatfintrack.py +++ b/tests/test_bharatfintrack.py @@ -39,6 +39,7 @@ def message(): 'error_date2': "time data '20-Se-2024' does not match format '%d-%b-%Y'", 'error_date3': 'Start date 27-Sep-2024 cannot be later than end date 26-Sep-2024.', 'error_excel': 'Input file extension ".xl" does not match the required ".xlsx".', + 'error_folder': 'The folder path does not exist.', 'error_index1': '"INVALID" index does not exist.', 'error_index2': '"NIFTY50 USD" index data is not available as open-source.' @@ -249,6 +250,19 @@ def test_index_download_historical_daily_data( assert float(df.iloc[-1, -1]) == expected_value +def test_download_daily_summary_report( + nse_index, + message +): + + # test for error when the input is a invalid folder path + with tempfile.TemporaryDirectory() as tmp_dir: + pass + with pytest.raises(Exception) as exc_info: + nse_index.download_daily_summary_report(tmp_dir) + assert exc_info.value.args[0] == message['error_folder'] + + def test_equity_cagr_from_launch( nse_index, capsys @@ -287,8 +301,7 @@ def test_sort_equity_cagr_from_launch( assert exc_info.value.args[0] == message['error_excel'] -@pytest.mark.filterwarnings('ignore::DeprecationWarning') -def test_all_index_cagr_from_inception( +def test_category_sort_equity_cagr_from_launch( nse_index, message ): @@ -296,7 +309,7 @@ def test_all_index_cagr_from_inception( # pass test with tempfile.TemporaryDirectory() as tmp_dir: excel_file = os.path.join(tmp_dir, 'equity.xlsx') - nse_index.all_equity_index_cagr_from_inception( + nse_index.category_sort_equity_cagr_from_launch( excel_file=excel_file ) df = pandas.read_excel(excel_file, index_col=[0, 1]) @@ -305,7 +318,73 @@ def test_all_index_cagr_from_inception( # error test for invalid Excel file input with pytest.raises(Exception) as exc_info: - nse_index.all_equity_index_cagr_from_inception( + nse_index.category_sort_equity_cagr_from_launch( excel_file='equily.xl' ) assert exc_info.value.args[0] == message['error_excel'] + + +def test_download_equity_indices_updated_value( + nse_tri, + message +): + + with tempfile.TemporaryDirectory() as tmp_dir: + excel_file = os.path.join(tmp_dir, 'equity.xlsx') + # pass test for downloading updated TRI values of NSE equity indices + nse_tri.download_equity_indices_updated_value( + excel_file=excel_file + ) + df = pandas.read_excel(excel_file) + assert df.shape[1] == 6 + # error test for invalid Excel file input + with pytest.raises(Exception) as exc_info: + nse_tri.download_equity_indices_updated_value( + excel_file='output.xl' + ) + assert exc_info.value.args[0] == message['error_excel'] + # pass test for sorting of NSE equity indices by TRI values + output_excel = os.path.join(tmp_dir, 'sorted_tri_value.xlsx') + nse_tri.sort_equity_value_from_launch( + input_excel=excel_file, + output_excel=output_excel + ) + df = pandas.read_excel(output_excel) + assert df.shape[1] == 5 + # error test for invalid Excel file input + with pytest.raises(Exception) as exc_info: + nse_tri.sort_equity_value_from_launch( + input_excel=excel_file, + output_excel='output.xl' + ) + assert exc_info.value.args[0] == message['error_excel'] + # pass test for sorting of NSE equity indices by CAGR (%) value + output_excel = os.path.join(tmp_dir, 'sorted_tri_cagr.xlsx') + nse_tri.sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel=output_excel + ) + df = pandas.read_excel(output_excel) + assert df.shape[1] == 9 + # error test for invalid Excel file input + with pytest.raises(Exception) as exc_info: + nse_tri.sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel='output.xl' + ) + assert exc_info.value.args[0] == message['error_excel'] + # pass test for categorical sorting NSE equity indices by CAGR (%) value + output_excel = os.path.join(tmp_dir, 'categorical_sorted_tri_cagr.xlsx') + nse_tri.category_sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel=output_excel + ) + df = pandas.read_excel(output_excel, index_col=[0, 1]) + assert len(df.index.get_level_values('Category').unique()) == 5 + # error test for invalid Excel file input + with pytest.raises(Exception) as exc_info: + nse_tri.category_sort_equity_cagr_from_launch( + input_excel=excel_file, + output_excel='output.xl' + ) + assert exc_info.value.args[0] == message['error_excel']