From a2c3c231e2ebba363eb62deced3faa2d1c9ce130 Mon Sep 17 00:00:00 2001 From: Debasish Pal <48341250+debpal@users.noreply.github.com> Date: Sun, 6 Oct 2024 20:22:59 +0300 Subject: [PATCH] version 0.1.3 --- BharatFinTrack/__init__.py | 2 +- BharatFinTrack/core.py | 3 +- BharatFinTrack/data/equity_indices.xlsx | Bin 24291 -> 24287 bytes BharatFinTrack/nse_index.py | 141 +++++----- BharatFinTrack/nse_tri.py | 340 +++++++++++++++++++++++- docs/changelog.rst | 20 +- docs/functionality.rst | 35 ++- docs/introduction.rst | 10 +- docs/quickstart.rst | 33 +-- tests/test_bharatfintrack.py | 87 +++++- 10 files changed, 575 insertions(+), 96 deletions(-) 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 99ebf97c5771a01cd75bccb7e7ede6fe25cc7436..c9467f5c6da64ff785c011918b3db9fa4b8f352d 100644 GIT binary patch delta 6492 zcmZ8lWmJ^i7M`I8>5^{v=p4GcyJ5%yq@+PWU}z~N2BaGSX$fgWkdk2t=|(zaP(lHL zi{7>Fx7K}s?X}Ny-t(Mu_S*aGXR8Oe-UDnr!Gh`B-`i!N0{{#l0Duqx00epPKlAc+ zckuFZ=L>r3QEqDORVGdrX6t&7{zD3rv>3!i7faNVxnwXq+R|lM{#j7B5iMJUT|*@I zzIUEz8f;P5RKW4>9ct5C4k^-c)x_-!9dI01p<)hoVu{W)F7JlK?%(hTz8I+@=QK)B zf~CjqzntA4`-b|k>Jba!k1?l_*PmnhoKcN7R_!{3YiOI0m}zH$Ye9sm)zNgQDU?1( z1+5<89ey8H!`Q7%r^Lv=Ni@b*$wSe?Y5rD{WYo8`?^bflWVM<6oTM{qB)}-gn2cPX zCkQ!frInZ*h4rk)P`zn{>6q>^xge&25GLm_uyL-3_GPgQSNBcTprJLebygybLhe0p zvRt{{v%=`HY=~A9{=A5E5ZkR@$PPpM?`J|D_d+ySuC10J#^55yGlE`;{Y&RjzsnVk zc}A0qOtMZx2_av@8rRR{T1x)-TJM}tG<-siMk@Fd9}n@K4lB)lQ$VInt1qU5J7Bqy zsn0|9@rDCvZCMm3w;+NRB!ldw#>E3+kPp=zub-!b^xMCGy7O#zoHQ@yk0)1EL~p8M zNcfzry33S{afT9!k<;T&mqJQAUsxu%5v-2wp0_gl>zGaspPeN3s8XdVvvucdG>+@N z<}`2&XLa*kQ@B@}i#dc?q}K8kpTMrcK15k~AqTQtu6-BQp@cg(s-pVZsoI^W3#lp! z`tH}pSidfY1vE<)j#CgodbJ7~{EFSlk5E$E6d#f;1rNOnIVQ>jY~xf_r{@JOheREW ziNasd7cVG0u<`ZQSAW@~)HRXBV0oIYGcdBnmPK9dLF83ed%r$>2|66K){~Fk~y} zWrJMkIu;5CH{7IXoR;MtajN4!>6NVrKr)#KO{SziV8 zR;l)Dia6YRs9wI_<03HTMH_7DBh62;4@~KBL%J?>DyfZS*|zI!&QVTqSD~fr%f@fX zV3y^#HlzB?YWS$bTnP#%M~e!%yZ zC*4ra$|93Do!^!NiiaIPJYRqW(%f{0?T>G8&XpXa?)W#v#2I}9nZ&`ty+qNkg-d zRBgP-m3m?=%WODB^lT5jdb25;pSL_eaWcoAdc2F;l&o3TRcX43b)eK^fZc*qzFe%a zA6SK}Bgf;Ho{_a_ygr_Zwz$3U!BWq=-C8+TD?5Is0gZzQ&yW&&Qv27&K}ti1mHtv z^xe@QLx8gE;IZ*pk{nojM!d=9uFcJAor9f$w*4aZs_P*9!raV*h$}&4Cb!*c_yLR7 zTr=YuiwDu_tM(R|7iFs&rZgsNR@f-3VG}wsa4?AtB=U`_Q~#N+^5M zLENetw}-8pk8x9Ep4`b121tydG23%tEj&;Z%;{s?5}EhZ!znHx zy-$bfl5dVmBf>0MoX0x&4d_*(*y%Qn?MGEo00k+5mRO#S;wxMU?=tls{8M+eD5Th+ z!?gYVIGGldRxs0V1B>p|MKm{J++oiCz}Z!^i_W18%9kc zw)jxr9#_J>n9DIo_7YhNn-7R1&|t^SYQF>Uan8>~w$H`8;4%kP}rpm750NG?W_M5qs37AY?g5qQc;tE-F$lIrc{r~lLS}We9U6HQwa3q zSNtJbu1f}Yo*hl4GY<&r(hkETnak8ptyG`cD^e9+cyAKss~9DjuNnz=vmjxf1u$@W(ODyf}k)TfTkNbRgcNX|u z&UTG|_;$HLF0V~#D|h_%=gvjp{Uu4>xb0*9`7DNeWZTWf<#o8rl9&BF?az76n=o7k z>UEDF*gtOY?RHY z76oCBSiw~5jU{_yvb)N6OurK!{KUSnKYKCF>&}?D2r;0ZQ)$JvP8OOo3-gRwu}G~v zEg9Vz!(W6u@e#|P3`d3*N`frDsgCqu2IImDHgS9tMn-yK5M2eRLn&yZfSx@-E$JGj&KvR+?|S&jurGR&g)Qi+%`}&?ILb+=SBEYE2WIhi*fH`P~}Ks$2`|> z7+w+Or7+u(V`$rKmrjjR>lhUCh>kqMHA!mL7 z7$jtd>}7@Hc{Y`99pat`<5OWm$e?f5u67;`Zm^3;o;36Y;-*7)h_kiIrJ76C_&HW{ zFeEAyW~@TiqEnl;OIeG~x7fsjr+so%xHLyNHcGiJLJ&`V-)zAYA00E@AzpVVFLAla z?23ZEnA8r@f1Sw4(PH&%rB4xNcfID+c_xneBDLGVR~`VHP8ro%I$v>5(}kxR^r<{O zd|)oBVA?XOst#5%rH`|rXAbU3>gSxx>z1e-y-JidWGv%p-yhfPNlIEfP0Z^);ND_g z=5#5f4Uz161A#F={r*|GBZM>Jqt4nn#G|kqPeK99G}Hsz*NGFG)w`A6FR$fb?6ggG zDnwFNUUieA?(IC(6Alh@LUTNNIW*?uz0C;sO=wyB!(QWyUrim>ypV$Yu3r!L3gb8( zjDPTG_3-A?2D^Jmy@;M(%OBG2hZpfu*|iV+++jFeIwK=15{w=hnmtwpYXa>Wxsh7J z`d9K55yS7x)Gjj6N!l$RE@K*R#VxWETL`y}L#Kz{Z+WffhlOxsR>M(S!&JaI89Ztq z@f#%7Q+FqYB_<(k?5uOBJ<=C-)63Q-{~737R`utC5xLn>N)HQa8N9?uzBvD*p!b5D^&_e6j*5|I8U+M3yv^>80q(r zYVD&VoY`b5%{*D=>{j@4;(UjL`pHDbZqvBI#?^>DZzh|Un8fuJbccOpDz1gi)p5+% z-+{UZAse1eU%qshE4T$HnU$!1c_5T5QkTQH1gCSif0M3BgkA)5iw>xyvuB=^C)`$_?YWb);+ zJAhZ!X&4)388|{vk8-57u1E--Xm*a4;vk{nxHRX`NMZOP&>#CP?R|gHSHQ zY|!nTGfb)3b#yc4rk-}Nnsz+8Ha;}wiIID;Z9d)k>Vlx-ZGlKKH5N0_Hw0GyX5})n zL0|{na5;irqiXZ{dq*938UH4Z^xbgvIW)D;?B&gO6)z`OnOhSHwwjT8!S(G;;jO%- z2=8McBA1z45AzEd1ox|rV~4RQAhMDr=)Nm*Y{Z8nE2{!&ApK*=ARFU7tKVS(wt|1< zOR^6yQj7Ud6CS^z!m5}0Rq!{1F$C;E4Q>nAo=a0Vm_ZFmVEf>MM+LSP7J+GdJTmOE> zuhV)%IDYlx$I+I*HQ)UFa*2?fn;G4(*2o>+rjk=l!M)vgxiC4jY@hpZ+Mih&Ik;3` zwh)ldc;bFsw{>+-P~TxF=&AfVgAz`|KyS)N3!xZF*E!%EGW+$$vbaBVWI-=j3_O0D zMeJ8+Phj5F0aKv=tvX7A=gkzFNgcaP&28&XgcemKzGIHX^4s34(C!QL;>_8Ctg zi({n%fqm3Un(HfKdWmE558S?v?`W9XsJDsOM;x1V1p8UqXkkqYFH@!PeQ`(~JF}sz zOh0v$WQ(K-Bv5g27KolWi5Sq?`-b@^TVyQKeiMR8t1h|N4`Aqa;mHTe%`|KRi(}Oh zJ21kXTB%L`mV}y+T<@D!whYHZE-H9jR{MKihs&Qv5rMPP=eu{I?{yaS(L^X_#axNq zzkHzI^PFu37L#<5ZN;X{K`FSUs>$QitebHp*ru77npg9N@IwYM1@4hb2fben0;i+b z@*1o`6`7X_EulZu>7j(|sEx4gjtD|Eset(=<&cyPlk}Js$5+(8A^D{(QP2+o#?B=eNif%(ppszjMH0kFfcELVZ{9o9sk7jgN(0uL0P6b5PGr@Ke6= z$U;LyRddc-@JvquBWmxWvi|DZD%MKc` z>jpu!^+Zv}=!lrdhnt(2<{0ZSvTI6P~iBtE$)t1 z#e`2`ugvxu_$*o1HB-Lqe0!3;3bxHM9m(n@vK9v3;CgclrTvOJdxk17Y|=nk!*vOV zR4H9AwHcs?GQyt7y#xuXf z_kd0(hlH!rZCt#(rGe?rq>UP^y67Crm>P4?f%eL83u%kQ`iv!6rbkYvsh{y?yo!B1 zak`pL_M{xRrj^GVDD#LS?S-GUmLY0$=GPgqjY`ke5os@aF?l&@@E3 zBP9?71z)}mpFL{!V2^(Ul1&w27J8^RAo#7I38vxpa7r%ROlS&b{98bivH24SH5GK~ z1Rk;#FE&(GZIfJUcMR@lc`W+11qxDk=94HPcI6D<$=?w5VAkX%)N23g>_hV!ME(dX z%~TZrmekU~&2OMq*kW5q$%Dm&%to6Y;R{cSw`!!Md>U2rJ&uvsEGJ%2i zUk3klV~^Ekxqs?pg|KnD8yw;0zVt5RPna{b4f1 zRY&@GkomEonc)7+^W3-9YoqAA$-RMN>})(13|jBZ`;(=Xgr(_euap5T6PrCs8O!$* z_Q!_S6z}*t#Bk?nS8<}DCXh;wo-?>NS{uS5-G?$3HRok!%kOIERnn8=&>+*U=W>ms z!Q0`RQ|=2JKWZt&J|x&~lUg`DoL-3NLdIpA{S0k*F3>#zNY8CV9C+VOWOw3e?J6=! z<7!8TO)#E2D$5rzURQ4~01|qm*pwou`eV{QHO<85OAxe(y&bnKk86(UUrh1o3!kez zY4SW;kymfjXOG=7MFiN%f zwbURPS3pjGJsJXmacSZpOh|J>qE;OilJLQPLXpl$?LlZ_DI#Dw>WVGQ?X?&DRzqmL zW_JcVCy&1s)VehbM}HFWDN>|Jb!#2K`CN+ar0Iun4V%?hA8o%^Meq8&vD%#kNZDOm z`_I?|$Uk6p+n+Xaw?6cju+{O8wt7#5FEwv8>OU8TjOH+;9nvGWinn@S&SGk&CFKrE zyiA@Wxi|s8YWydkL|GHe6m3qWdMmbgRkEH2V?-G0CFX6OpvNIhoq*rn%(_!F*hb9s z8W>m|{A_;%S}kFz#{3}j`5gQg3-iCF&sg$RXhD_hX_}f<{`Tj2)`!<+#YT(OQseQm zNihq4_B6$Pl&0qjsiF20Wa^miKc?C!T$54xY`k3k{k+L4<|IO<4b!6X7rU zh6ygeb3VIf_^)1NDrQE5X_cPIuhHCO$PR=}X$}aG#i20QOSXG;rpGx;zR3^Q)8gfG ztPO5^JQ8))C|zA!Ygu)3ipn~;-#dCnFuIFvt!ismP_6ySMNuC^)z~n{`cUVSd9$3VoGPB$rzX)CxDYb+Awh#Z7gX4|K%fI*ZWk+98>cOEcI5@E zY;`j?iLe(vpG0VYS~_dRZv|~Dgt3&=FcD}0TWUgAk1WB!ACl50O@wvQ*9O9D_R|fF zxM8&e+W)KyS|!TTE@4;3IeY>9|T}|08zs;Es5ZcmefE99A+sG z#D(`+GSd97&`mI`P}VsJqS4ICzh`)7-Ab8#ZPe^Dype+R3rz-a0c zf6W^!Fi;CF0MQ5P!XqL2Xa>^oDGgTmPY6HSko;d$+nOIuO69K^p@Ipou;v0b!l$f_ s(b#nVI<;)bY5vA75CE`70|1EsUGBlkg+I5E1yaL1ZCJ3*%>E4h2Udg-(EtDd delta 6527 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 Oh!#6`JdF82bNmk#Loc)d 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']