vibhorag101 commited on
Commit
dfbd37e
1 Parent(s): e84070c

Refactored the code, and added XIRR

Browse files
Files changed (4) hide show
  1. app.py +47 -136
  2. portfolio.py +83 -0
  3. returns.py +71 -0
  4. utils.py +37 -0
app.py CHANGED
@@ -1,9 +1,16 @@
 
1
  import gradio as gr
 
 
 
2
  import pandas as pd
3
  import plotly.graph_objects as go
4
- from datetime import datetime, timedelta
5
- import requests
6
- import locale
 
 
 
7
 
8
  js_func = """
9
  function refresh() {
@@ -18,114 +25,38 @@ function refresh() {
18
 
19
  locale.setlocale(locale.LC_MONETARY, 'en_IN')
20
 
21
- # def create_pie_chart(schemes):
22
- # labels = list(schemes.keys())
23
- # values = list(schemes.values())
24
-
25
- # fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
26
- # fig.update_layout(title_text="Scheme Weightages")
27
- # return fig
28
-
29
-
30
- def get_nav_data(scheme_code):
31
- url = f"https://api.mfapi.in/mf/{scheme_code}"
32
- response = requests.get(url)
33
- data = response.json()
34
- df = pd.DataFrame(data['data'])
35
- df['date'] = pd.to_datetime(df['date'], format='%d-%m-%Y')
36
- df['nav'] = df['nav'].astype(float)
37
- df = df.sort_values('date')
38
- inception_date = df['date'].min()
39
- return df, inception_date
40
-
41
- def calculate_sip_returns(nav_data, sip_amount, upfront_amount, stepup, start_date, end_date, SIP_Date):
42
- start_date = pd.Timestamp(start_date)
43
- end_date = pd.Timestamp(end_date)
44
-
45
- nav_data_filtered = nav_data[(nav_data['date'] >= start_date) & (nav_data['date'] <= end_date)].copy()
46
- nav_data_filtered['date'] = pd.to_datetime(nav_data_filtered['date'])
47
- if SIP_Date == 'start':
48
- last_dates = nav_data_filtered.groupby([nav_data_filtered['date'].dt.year, nav_data_filtered['date'].dt.month]).head(1)
49
- elif SIP_Date == 'end':
50
- last_dates = nav_data_filtered.groupby([nav_data_filtered['date'].dt.year, nav_data_filtered['date'].dt.month]).tail(1)
51
- else:
52
- last_dates = nav_data_filtered.groupby([nav_data_filtered['date'].dt.year, nav_data_filtered['date'].dt.month]).apply(lambda x: x.iloc[len(x)//2])
53
-
54
- total_investment = upfront_amount
55
- current_sip_amount = sip_amount
56
-
57
- # do calculation for upfront investment
58
- units_bought = upfront_amount / nav_data_filtered.iloc[0]['nav']
59
- units_accumulated = units_bought
60
- previous_year = start_date.year
61
-
62
- for _, row in last_dates.iloc[:-1].iterrows():
63
- # Check if a year has passed and increase SIP amount accordingly
64
- if row['date'].year > previous_year:
65
- current_sip_amount += current_sip_amount * (stepup / 100)
66
- previous_year = row['date'].year
67
-
68
- units_bought = current_sip_amount / row['nav']
69
- units_accumulated += units_bought
70
- total_investment += current_sip_amount
71
-
72
- final_value = units_accumulated * last_dates.iloc[-1]['nav']
73
- total_return = (final_value - total_investment) / total_investment * 100
74
-
75
- return total_return, final_value, total_investment
76
-
77
-
78
- def calculate_portfolio_returns(schemes, sip_amount, upfront_amount, stepup, start_date, end_date, SIP_date, schemes_df):
79
- scheme_returns = []
80
- total_investment = 0
81
- final_value = 0
82
- inception_dates = []
83
-
84
- for scheme_name, scheme_weight in schemes.items():
85
- scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
86
- nav_data, inception_date = get_nav_data(scheme_code)
87
- inception_dates.append((scheme_name, inception_date))
88
- scheme_return, scheme_final_value, scheme_total_investment = calculate_sip_returns(nav_data, sip_amount * scheme_weight / 100, upfront_amount * scheme_weight / 100, stepup, start_date, end_date, SIP_date)
89
- scheme_returns.append((scheme_name, scheme_return,scheme_final_value,scheme_total_investment))
90
- final_value += scheme_final_value
91
- total_investment += scheme_total_investment
92
-
93
- portfolio_return = (final_value - total_investment) / total_investment * 100
94
- return portfolio_return, final_value, total_investment, scheme_returns, inception_dates
95
-
96
- def update_sip_calculator(*args):
97
  period = args[0]
98
  custom_start_date = args[1]
99
  custom_end_date = args[2]
100
  SIP_Date = args[3]
101
  sip_amount = args[4]
102
- upfront_amount = args[5]
103
  stepup = args[6]
104
  schemes_df = args[7]
105
- schemes = {}
 
106
 
107
- for i in range(8, len(args) - 1, 2): # Adjust range to account for use_inception_date
108
  if args[i] and args[i+1]:
109
- schemes[args[i]] = float(args[i+1])
110
 
111
- use_inception_date = args[-1] # Get use_inception_date from the last argument
112
 
113
- if not schemes:
114
  return "Please add at least one scheme.", None, None, None
115
-
116
- total_weight = sum(schemes.values())
117
-
118
- end_date = datetime.now().date()
119
 
120
  if use_inception_date:
121
- start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
122
  elif period == "Custom":
123
  if not custom_start_date or not custom_end_date:
124
  return "Please provide both start and end dates for custom period.", None, None, None
125
- start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
126
- end_date = datetime.strptime(custom_end_date, "%Y-%m-%d").date()
127
  elif period == "YTD":
128
- start_date = datetime(end_date.year, 1, 1).date()
129
  elif not period:
130
  return "Please select a period, provide custom dates, or use the inception date.", None, None, None
131
  else:
@@ -135,47 +66,28 @@ def update_sip_calculator(*args):
135
 
136
  if 'year' in period_parts[1]:
137
  years = int(period_parts[0])
138
- start_date = end_date - timedelta(days=years*365)
139
  else:
140
  months = int(period_parts[0])
141
- start_date = end_date - timedelta(days=months*30)
142
 
143
- try:
144
- portfolio_return, final_value, total_investment, scheme_returns, inception_dates = calculate_portfolio_returns(schemes, sip_amount, upfront_amount,stepup, start_date, end_date, SIP_Date, schemes_df)
145
- except Exception as e:
146
- return f"Error: {str(e)}", None, None, None
147
 
148
- # Check if start_date is before any scheme's inception date
149
  inception_warnings = []
150
- earliest_inception_date = max(inception_date for _, inception_date in inception_dates)
151
  for scheme_name, inception_date in inception_dates:
152
- if start_date < inception_date.date():
153
- inception_warnings.append(f"Warning: {scheme_name} inception date ({inception_date.date()}) is after the chosen start date ({start_date}).")
154
-
155
- result = ""
156
- if inception_warnings:
157
- result += "The following warnings were found:\n"
158
- result += "\n".join(inception_warnings) + "\n\n"
159
- result += f"Possible start date for all chosen schemes is: {earliest_inception_date.date()}\n\n"
160
-
161
- result += f"Portfolio Absolute return: {portfolio_return:.2f}%\n"
162
- result += f"Total investment: {locale.currency(total_investment,grouping=True)}\n"
163
- result += f"Final value: {locale.currency(final_value,grouping=True)}\n\n"
164
- result += "Individual scheme returns:\n"
165
- for scheme_name, scheme_return, scheme_final_value, scheme_total_investment in scheme_returns:
166
- result += f"---- {scheme_name} ----:\n"
167
- result += f"Return: {scheme_return:.2f}%\n"
168
- result += f"Total investment: {locale.currency(scheme_total_investment,grouping=True)}\n"
169
- result += f"Final value: {locale.currency(scheme_final_value,grouping=True)}\n\n"
170
- # pie_chart = create_pie_chart(schemes)
171
- # return result, pie_chart, final_value, total_investment
172
- return result
173
-
174
- def fetch_scheme_data():
175
- url = "https://api.mfapi.in/mf"
176
- response = requests.get(url)
177
- schemes = response.json()
178
- return pd.DataFrame(schemes)
179
 
180
  def quick_search_schemes(query, schemes_df):
181
  if not query:
@@ -271,9 +183,8 @@ def handle_row_selection(schemes_list, evt: gr.SelectData, table_data):
271
  return table_data, schemes_list
272
 
273
  def create_ui():
274
- schemes_df = fetch_scheme_data()
275
-
276
- with gr.Blocks(js=js_func) as app:
277
  gr.Markdown("# Mutual Fund SIP Returns Calculator")
278
 
279
  with gr.Row():
@@ -358,7 +269,7 @@ def create_ui():
358
  inception_dates = []
359
  for scheme_name, _ in schemes_list:
360
  scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
361
- _, inception_date = get_nav_data(scheme_code)
362
  inception_dates.append(inception_date)
363
  return max(inception_dates).strftime("%Y-%m-%d") if inception_dates else ""
364
 
@@ -387,14 +298,14 @@ def create_ui():
387
  return inputs
388
 
389
  calculate_button.click(
390
- lambda *args: update_sip_calculator(*prepare_inputs_with_inception(*args)),
391
  inputs=[period, custom_start_date, custom_end_date, SIP_Date, sip_amount,upfront_amount,stepup,schemes_list, gr.State(schemes_df), use_inception_date, inception_date_display],
392
  outputs=[result]
393
  # outputs=[result, final_value, total_investment]
394
  # outputs=[result, pie_chart, final_value, total_investment]
395
  )
396
 
397
- return app
398
 
399
- app = create_ui()
400
- app.launch()
 
1
+ import locale
2
  import gradio as gr
3
+ import matplotlib.pyplot as plt
4
+ import numpy as np
5
+ import numpy_financial as npf
6
  import pandas as pd
7
  import plotly.graph_objects as go
8
+ import seaborn as sns
9
+ from pandas.tseries.offsets import DateOffset, MonthEnd
10
+ from scipy import optimize
11
+
12
+ from portfolio import calculate_portfolio_returns
13
+ from utils import get_all_mf_schemes_df,get_mf_scheme_data
14
 
15
  js_func = """
16
  function refresh() {
 
25
 
26
  locale.setlocale(locale.LC_MONETARY, 'en_IN')
27
 
28
+ def get_portfolio_report(*args):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  period = args[0]
30
  custom_start_date = args[1]
31
  custom_end_date = args[2]
32
  SIP_Date = args[3]
33
  sip_amount = args[4]
34
+ lumpsum_amount = args[5]
35
  stepup = args[6]
36
  schemes_df = args[7]
37
+
38
+ scheme_name_and_weight = {}
39
 
40
+ for i in range(8, len(args) - 1, 2):
41
  if args[i] and args[i+1]:
42
+ scheme_name_and_weight[args[i]] = float(args[i+1])
43
 
44
+ use_inception_date = args[-1]
45
 
46
+ if not scheme_name_and_weight:
47
  return "Please add at least one scheme.", None, None, None
48
+
49
+ end_date = pd.Timestamp.now().floor('D')
 
 
50
 
51
  if use_inception_date:
52
+ start_date = pd.Timestamp(custom_start_date)
53
  elif period == "Custom":
54
  if not custom_start_date or not custom_end_date:
55
  return "Please provide both start and end dates for custom period.", None, None, None
56
+ start_date = pd.Timestamp(custom_start_date)
57
+ end_date = pd.Timestamp(custom_end_date)
58
  elif period == "YTD":
59
+ start_date = pd.Timestamp(f"{end_date.year}-01-01")
60
  elif not period:
61
  return "Please select a period, provide custom dates, or use the inception date.", None, None, None
62
  else:
 
66
 
67
  if 'year' in period_parts[1]:
68
  years = int(period_parts[0])
69
+ start_date = end_date - DateOffset(years=years)
70
  else:
71
  months = int(period_parts[0])
72
+ start_date = end_date - DateOffset(months=months)
73
 
74
+ portfolio_return_string, inception_dates, scheme_individual_returns = calculate_portfolio_returns(scheme_name_and_weight, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_Date, schemes_df)
 
 
 
75
 
 
76
  inception_warnings = []
 
77
  for scheme_name, inception_date in inception_dates:
78
+ if start_date < inception_date:
79
+ inception_warnings.append(f"Warning: {scheme_name} inception date ({inception_date.date()}) is after the chosen start date ({start_date.date()}).")
80
+
81
+ result_string = f"""
82
+ {portfolio_return_string}
83
+ ------------------------------------
84
+ Individual Scheme Returns
85
+ ------------------------------------
86
+ {''.join(scheme_individual_returns)}
87
+ {''.join(inception_warnings)}
88
+ """
89
+ return result_string
90
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
  def quick_search_schemes(query, schemes_df):
93
  if not query:
 
183
  return table_data, schemes_list
184
 
185
  def create_ui():
186
+ schemes_df = get_all_mf_schemes_df()
187
+ with gr.Blocks(js=js_func) as demo:
 
188
  gr.Markdown("# Mutual Fund SIP Returns Calculator")
189
 
190
  with gr.Row():
 
269
  inception_dates = []
270
  for scheme_name, _ in schemes_list:
271
  scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
272
+ _, inception_date = get_mf_scheme_data(scheme_code)
273
  inception_dates.append(inception_date)
274
  return max(inception_dates).strftime("%Y-%m-%d") if inception_dates else ""
275
 
 
298
  return inputs
299
 
300
  calculate_button.click(
301
+ lambda *args: get_portfolio_report(*prepare_inputs_with_inception(*args)),
302
  inputs=[period, custom_start_date, custom_end_date, SIP_Date, sip_amount,upfront_amount,stepup,schemes_list, gr.State(schemes_df), use_inception_date, inception_date_display],
303
  outputs=[result]
304
  # outputs=[result, final_value, total_investment]
305
  # outputs=[result, pie_chart, final_value, total_investment]
306
  )
307
 
308
+ return demo
309
 
310
+ demo = create_ui()
311
+ demo.launch(debug=True)
portfolio.py ADDED
@@ -0,0 +1,83 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ from utils import get_mf_scheme_data
3
+ from returns import get_investment_xirr, get_investment_sip_absolute_returns
4
+
5
+ def get_portfolio_nav_df(schemes_name_and_weight, start_date, end_date, schemes_df):
6
+ # start_date = pd.to_datetime(start_date)
7
+ # end_date = pd.to_datetime(end_date)
8
+
9
+ portfolio_nav_df = pd.DataFrame()
10
+
11
+ for scheme_name, scheme_weight in schemes_name_and_weight.items():
12
+ scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
13
+ scheme_nav_df, _ = get_mf_scheme_data(scheme_code= scheme_code)
14
+ scheme_nav_df = scheme_nav_df[(scheme_nav_df['date'] >= start_date) & (scheme_nav_df['date'] <= end_date)]
15
+ scheme_nav_df['nav'] = scheme_nav_df['nav'] * scheme_weight / 100
16
+ if portfolio_nav_df.empty:
17
+ portfolio_nav_df = scheme_nav_df
18
+ else:
19
+ portfolio_nav_df['nav'] += scheme_nav_df['nav']
20
+
21
+ return portfolio_nav_df
22
+
23
+
24
+ def get_portfolio_xirr_returns(portfolio_df, start_date, end_date, SIP_date, lumpsum_amount, sip_amount):
25
+ return(get_investment_xirr(portfolio_df, start_date, end_date, SIP_date, lumpsum_amount, sip_amount))
26
+
27
+ def get_portfolio_absolute_returns(portfolio_df, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_date):
28
+
29
+ return (get_investment_sip_absolute_returns(portfolio_df, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_date))
30
+
31
+ def get_invdividual_scheme_returns(scheme_df, scheme_name,scheme_sip_amount, scheme_lumpsum_amount, stepup, start_date, end_date, SIP_date):
32
+ absolute_return, final_value, invested_value = get_investment_sip_absolute_returns(scheme_df, scheme_sip_amount, scheme_lumpsum_amount, stepup, start_date, end_date, SIP_date)
33
+ xirr = get_investment_xirr(scheme_df, start_date, end_date, SIP_date, scheme_lumpsum_amount, scheme_sip_amount)
34
+ scheme_string = f"""
35
+ Scheme: {scheme_name}
36
+ ------------------------------------
37
+ Investment: {invested_value}
38
+ Scheme Final Value: {final_value}
39
+ Absolute Return: {absolute_return}%
40
+ XIRR: {xirr}%\n
41
+ """
42
+ return (scheme_string)
43
+
44
+
45
+
46
+ def calculate_portfolio_returns(scheme_name_and_weight, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_date, schemes_df):
47
+ inception_dates = []
48
+ scheme_individual_returns = []
49
+ portfolio_df = get_portfolio_nav_df(scheme_name_and_weight, start_date, end_date,schemes_df)
50
+ portfolio_absolute_return, portfolio_final_value, portfolio_invested_value = get_portfolio_absolute_returns(portfolio_df, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_date)
51
+ portfolio_return_string = f"""
52
+ Portfolio
53
+ ------------------------------------
54
+ SIP Amount: {sip_amount}
55
+ Lumpsum Amount: {lumpsum_amount}
56
+ Stepup: {stepup}
57
+ Start Date: {start_date}
58
+ End Date: {end_date}
59
+ SIP Date: {SIP_date}
60
+ Total Investment: {portfolio_invested_value}
61
+ ------------------------------------
62
+ Portfolio Returns
63
+ XIRR = {get_portfolio_xirr_returns(portfolio_df, start_date, end_date, SIP_date, lumpsum_amount, sip_amount)}%
64
+ Absolute Returns = {portfolio_absolute_return}%
65
+ Portfolio Final Value = {portfolio_final_value}
66
+ """
67
+
68
+ for scheme_name, scheme_weight in scheme_name_and_weight.items():
69
+ scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
70
+ scheme_df, scheme_inception_date = get_mf_scheme_data(scheme_code)
71
+ inception_dates.append((scheme_name, scheme_inception_date))
72
+ scheme_sip_amount = sip_amount * scheme_weight / 100
73
+ scheme_lumpsum_amount = lumpsum_amount * scheme_weight / 100
74
+ scheme_individual_returns.append(get_invdividual_scheme_returns(scheme_df, scheme_name, scheme_sip_amount, scheme_lumpsum_amount, stepup, start_date, end_date, SIP_date))
75
+
76
+ return portfolio_return_string, inception_dates, scheme_individual_returns
77
+
78
+
79
+
80
+
81
+
82
+
83
+
returns.py ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from scipy import optimize
2
+ from utils import get_monthly_sip_nav_df
3
+
4
+
5
+ def calculate_xnpv(rate, cashflows):
6
+ chron_order = sorted(cashflows, key=lambda x: x[0])
7
+ t0 = chron_order[0][0]
8
+ return sum([cf/(1+rate)**((t-t0).days/365.0) for (t,cf) in chron_order])
9
+
10
+ def calculate_xirr(cashflows, guess=0.1):
11
+ return optimize.newton(lambda r: calculate_xnpv(r,cashflows), guess)
12
+
13
+ def get_investment_xirr(investment_df, start_date, end_date, SIP_date, lumpsum_amount=0, sip_amount=1000):
14
+ # Get the monthly NAVs
15
+ monthly_nav_df = get_monthly_sip_nav_df(investment_df, start_date, end_date, SIP_date)
16
+
17
+ # Initialize lists to store cash flows and their corresponding dates
18
+ cash_flows = []
19
+ dates = []
20
+
21
+ # Add the lumpsum investment at the start
22
+ cash_flows.append(-lumpsum_amount)
23
+ dates.append(start_date)
24
+
25
+ # Calculate initial units from lumpsum investment
26
+ initial_units = lumpsum_amount / monthly_nav_df['nav'].iloc[0]
27
+ total_units = initial_units
28
+
29
+ # Iterate over each row and record the SIP investments
30
+ for _, row in monthly_nav_df.iterrows():
31
+ cash_flows.append(-sip_amount) # SIP investment is negative cash flow
32
+ dates.append(row['date'])
33
+ total_units += sip_amount / row['nav']
34
+
35
+ # Add the final value as a positive cash flow
36
+ final_value = total_units * monthly_nav_df['nav'].iloc[-1]
37
+ cash_flows.append(final_value)
38
+ dates.append(monthly_nav_df['date'].iloc[-1])
39
+
40
+ portfolio_XIRR = calculate_xirr(list(zip(dates, cash_flows)))
41
+
42
+ return portfolio_XIRR * 100
43
+
44
+ def get_investment_sip_absolute_returns(investment_df, sip_amount, lumpsum_amount, stepup, start_date, end_date, SIP_Date):
45
+ # start_date = pd.Timestamp(start_date)
46
+ # end_date = pd.Timestamp(end_date)
47
+
48
+ scheme_df_monthly = get_monthly_sip_nav_df(investment_df, start_date, end_date, SIP_Date)
49
+
50
+ total_investment = lumpsum_amount
51
+ current_sip_amount = sip_amount
52
+
53
+ # do calculation for upfront investment
54
+ units_bought = lumpsum_amount / scheme_df_monthly.iloc[0]['nav']
55
+ units_accumulated = units_bought
56
+ previous_year = start_date.year
57
+
58
+ for _, row in scheme_df_monthly.iloc[:-1].iterrows():
59
+ # Check if a year has passed and increase SIP amount accordingly
60
+ if row['date'].year > previous_year:
61
+ current_sip_amount += current_sip_amount * (stepup / 100)
62
+ previous_year = row['date'].year
63
+
64
+ units_bought = current_sip_amount / row['nav']
65
+ units_accumulated += units_bought
66
+ total_investment += current_sip_amount
67
+
68
+ final_value = units_accumulated * scheme_df_monthly.iloc[-1]['nav']
69
+ total_return = (final_value - total_investment) / total_investment * 100
70
+
71
+ return total_return, final_value, total_investment
utils.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import requests
3
+
4
+ # This function groups the data by month
5
+ # and returns the first, last or middle data of each month
6
+ def get_monthly_sip_nav_df(df, start_date, end_date, SIP_date):
7
+ df = df.copy()
8
+ df['date'] = pd.to_datetime(df['date'])
9
+ df = df[(df['date'] >= start_date) & (df['date'] <= end_date)]
10
+ if SIP_date == 'start':
11
+ df = df.groupby([df['date'].dt.year, df['date'].dt.month]).first()
12
+ elif SIP_date == 'end':
13
+ df = df.groupby([df['date'].dt.year, df['date'].dt.month]).last()
14
+ else:
15
+ df = df.groupby([df['date'].dt.year, df['date'].dt.month]).apply(lambda x: x.iloc[len(x)//2])
16
+ return df
17
+
18
+
19
+ # This function returns the complete list of mutual fund schemes in a DataFrame
20
+ def get_all_mf_schemes_df():
21
+ url = "https://api.mfapi.in/mf"
22
+ response = requests.get(url)
23
+ schemes = response.json()
24
+ return pd.DataFrame(schemes)
25
+
26
+ # This function returns the data of a particular mutual fund scheme, and its inception date
27
+ def get_mf_scheme_data(scheme_code):
28
+ url = f"https://api.mfapi.in/mf/{scheme_code}"
29
+ response = requests.get(url)
30
+ data = response.json()
31
+ df = pd.DataFrame(data['data'])
32
+ df['date'] = pd.to_datetime(df['date'], format='%d-%m-%Y')
33
+ df['nav'] = df['nav'].astype(float)
34
+ df = df.sort_values('date')
35
+ inception_date = df['date'].min()
36
+ return df, inception_date
37
+