vibhorag101 commited on
Commit
faecf2c
1 Parent(s): e4e4d06
Files changed (2) hide show
  1. app.py +276 -0
  2. requirements.txt +6 -0
app.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+
7
+ def get_nav_data(scheme_code):
8
+ url = f"https://api.mfapi.in/mf/{scheme_code}"
9
+ response = requests.get(url)
10
+ data = response.json()
11
+ df = pd.DataFrame(data['data'])
12
+ df['date'] = pd.to_datetime(df['date'], format='%d-%m-%Y')
13
+ df['nav'] = df['nav'].astype(float)
14
+ df = df.sort_values('date')
15
+ return df
16
+
17
+ def calculate_sip_returns(nav_data, sip_amount, start_date, end_date,SIP_Date):
18
+ start_date = pd.Timestamp(start_date)
19
+ end_date = pd.Timestamp(end_date)
20
+
21
+ nav_data_filtered = nav_data[(nav_data['date'] >= start_date) & (nav_data['date'] <= end_date)].copy()
22
+ nav_data_filtered['date'] = pd.to_datetime(nav_data_filtered['date'])
23
+ if SIP_Date == 'start':
24
+ last_dates = nav_data_filtered.groupby([nav_data_filtered['date'].dt.year, nav_data_filtered['date'].dt.month]).head(1)
25
+ elif SIP_Date == 'end':
26
+ last_dates = nav_data_filtered.groupby([nav_data_filtered['date'].dt.year, nav_data_filtered['date'].dt.month]).tail(1)
27
+ else:
28
+ 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])
29
+
30
+ units_accumulated = 0
31
+ total_investment = 0
32
+
33
+ for _, row in last_dates.iloc[:-1].iterrows():
34
+ units_bought = sip_amount / row['nav']
35
+ units_accumulated += units_bought
36
+ total_investment += sip_amount
37
+
38
+ final_value = units_accumulated * last_dates.iloc[-1]['nav']
39
+ total_return = (final_value - total_investment) / total_investment * 100
40
+
41
+ return total_return, final_value, total_investment
42
+
43
+ def create_pie_chart(schemes):
44
+ labels = list(schemes.keys())
45
+ values = list(schemes.values())
46
+
47
+ fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
48
+ fig.update_layout(title_text="Scheme Weightages")
49
+ return fig
50
+
51
+ def calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_date,schemes_df):
52
+ scheme_returns = []
53
+ total_investment = 0
54
+ final_value = 0
55
+
56
+ for scheme_name, scheme_weight in schemes.items():
57
+ scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
58
+ nav_data = get_nav_data(scheme_code)
59
+ scheme_return, scheme_final_value, scheme_total_investment = calculate_sip_returns(nav_data, sip_amount * scheme_weight / 100, start_date, end_date,SIP_date)
60
+ scheme_returns.append((scheme_name, scheme_return))
61
+ final_value += scheme_final_value
62
+ total_investment += scheme_total_investment
63
+
64
+ portfolio_return = (final_value - total_investment) / total_investment * 100
65
+ return portfolio_return, final_value, total_investment, scheme_returns
66
+
67
+ def update_sip_calculator(*args):
68
+ period = args[0]
69
+ custom_start_date = args[1]
70
+ custom_end_date = args[2]
71
+ SIP_Date = args[3]
72
+ sip_amount = args[4]
73
+ schemes_df = args[5]
74
+ schemes = {}
75
+
76
+ for i in range(6, len(args), 2):
77
+ if args[i] and args[i+1]:
78
+ schemes[args[i]] = float(args[i+1])
79
+
80
+ if not schemes:
81
+ return "Please add at least one scheme.", None, None, None
82
+
83
+ total_weight = sum(schemes.values())
84
+
85
+ end_date = datetime.now().date()
86
+ if period == "Custom":
87
+ if not custom_start_date or not custom_end_date:
88
+ return "Please provide both start and end dates for custom period.", None, None, None
89
+ start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
90
+ end_date = datetime.strptime(custom_end_date, "%Y-%m-%d").date()
91
+ else:
92
+ years = int(period.split()[0])
93
+ start_date = end_date - timedelta(days=years*365)
94
+
95
+ try:
96
+ portfolio_return, final_value, total_investment, scheme_returns = calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_Date,schemes_df)
97
+ except Exception as e:
98
+ return f"Error: {str(e)}", None, None, None
99
+
100
+ result = f"Total portfolio SIP return: {portfolio_return:.2f}%\n"
101
+ result += f"Total investment: ₹{total_investment:.2f}\n"
102
+ result += f"Final value: ₹{final_value:.2f}\n\n"
103
+ result += "Individual scheme returns:\n"
104
+ for scheme_name, scheme_return in scheme_returns:
105
+ result += f"{scheme_name}: {scheme_return:.2f}%\n"
106
+
107
+ pie_chart = create_pie_chart(schemes)
108
+
109
+ return result, pie_chart, final_value, total_investment
110
+
111
+ def fetch_scheme_data():
112
+ url = "https://api.mfapi.in/mf"
113
+ response = requests.get(url)
114
+ schemes = response.json()
115
+ return pd.DataFrame(schemes)
116
+
117
+ def quick_search_schemes(query, schemes_df):
118
+ if not query:
119
+ return []
120
+ matching_schemes = schemes_df[schemes_df['schemeName'].str.contains(query, case=False, na=False)]
121
+ return matching_schemes['schemeName'].tolist()[:40]
122
+
123
+ def update_scheme_dropdown(query, schemes_df, key_up_data: gr.KeyUpData):
124
+ schemes = quick_search_schemes(key_up_data.input_value, schemes_df)
125
+ return gr.update(choices=schemes, visible=True)
126
+
127
+ def update_schemes_list(schemes_list, updated_data):
128
+ new_schemes_list = []
129
+ for _, row in updated_data.iterrows():
130
+ scheme_name = row.get('Scheme Name')
131
+ weight = row.get('Weight (%)')
132
+ action = row.get('Actions')
133
+ if scheme_name and weight is not None and action != '🗑️': # Only keep rows that aren't marked for deletion
134
+ try:
135
+ weight_float = float(weight)
136
+ new_schemes_list.append((scheme_name, weight_float))
137
+ except ValueError:
138
+ # If weight is not a valid float, skip this row
139
+ continue
140
+ return new_schemes_list
141
+
142
+ def update_schemes_table(schemes_list):
143
+ df = pd.DataFrame(schemes_list, columns=["Scheme Name", "Weight (%)"])
144
+ df["Actions"] = "❌" # Use a different emoji to avoid confusion with the deletion mark
145
+ return df
146
+
147
+ def add_scheme_to_list(schemes_list, scheme_name, weight):
148
+ if scheme_name and weight:
149
+ new_list = schemes_list + [(scheme_name, float(weight))]
150
+ return new_list, update_schemes_table(new_list), None, 0
151
+ return schemes_list, update_schemes_table(schemes_list), scheme_name, weight
152
+
153
+ def update_schemes(schemes_list, updated_data):
154
+ try:
155
+ new_schemes_list = update_schemes_list(schemes_list, updated_data)
156
+ if not new_schemes_list:
157
+ return schemes_list, update_schemes_table(schemes_list), "No valid schemes found in the table."
158
+ return new_schemes_list, update_schemes_table(new_schemes_list), None
159
+ except Exception as e:
160
+ error_msg = f"Error updating schemes: {str(e)}"
161
+ return schemes_list, update_schemes_table(schemes_list), error_msg
162
+
163
+ def prepare_inputs(period, custom_start, custom_end,SIP_Date,sip_amount, schemes_list, schemes_df,):
164
+ inputs = [period, custom_start, custom_end,SIP_Date, sip_amount, schemes_df]
165
+ for name, weight in schemes_list:
166
+ inputs.extend([name, weight])
167
+ return inputs
168
+
169
+ def handle_row_selection(schemes_list, evt: gr.SelectData, table_data):
170
+ # print(f"Event data: {evt}")
171
+ # print(f"Event index: {evt.index}")
172
+ # print(f"Table data: {table_data}")
173
+
174
+ if evt.index is not None and len(evt.index) > 1:
175
+ column_index = evt.index[1]
176
+ if column_index == 2: # "Actions" column
177
+ row_index = evt.index[0]
178
+ # Remove the row instead of marking it
179
+ table_data = table_data.drop(row_index).reset_index(drop=True)
180
+ # Update the schemes_list
181
+ updated_schemes_list = [(row['Scheme Name'], row['Weight (%)']) for _, row in table_data.iterrows()]
182
+ return table_data, updated_schemes_list
183
+ return table_data, schemes_list
184
+
185
+ def update_schemes_table(schemes_list):
186
+ df = pd.DataFrame(schemes_list, columns=["Scheme Name", "Weight (%)"])
187
+ df["Actions"] = "❌"
188
+ return df
189
+
190
+ def create_ui():
191
+ schemes_df = fetch_scheme_data()
192
+
193
+ with gr.Blocks() as app:
194
+ gr.Markdown("# Mutual Fund SIP Returns Calculator")
195
+
196
+ with gr.Row():
197
+ period = gr.Dropdown(choices=["1 year", "3 years", "5 years", "7 years", "10 years", "Custom"], label="Select Period")
198
+ custom_start_date = gr.Textbox(label="Custom Start Date (YYYY-MM-DD)", visible=False)
199
+ custom_end_date = gr.Textbox(label="Custom End Date (YYYY-MM-DD)", visible=False)
200
+ SIP_Date = gr.Dropdown(label="SIP Date", choices=["start","middle","end"])
201
+
202
+ sip_amount = gr.Number(label="SIP Amount (₹)")
203
+
204
+ schemes_list = gr.State([])
205
+
206
+ with gr.Row():
207
+ scheme_dropdown = gr.Dropdown(label="Select Scheme", choices=[], allow_custom_value=True, interactive=True)
208
+ scheme_weight = gr.Slider(minimum=0, maximum=100, step=1, label="Scheme Weight (%)")
209
+ add_button = gr.Button("Add Scheme")
210
+
211
+ schemes_table = gr.Dataframe(
212
+ headers=["Scheme Name", "Weight (%)", "Actions"],
213
+ datatype=["str", "number", "str"],
214
+ col_count=(3, "fixed"),
215
+ label="Added Schemes",
216
+ type="pandas",
217
+ interactive=True
218
+ )
219
+
220
+ update_button = gr.Button("Update Schemes")
221
+ error_message = gr.Textbox(label="Error", visible=False)
222
+
223
+ calculate_button = gr.Button("Calculate Returns")
224
+
225
+ result = gr.Textbox(label="Results")
226
+ pie_chart = gr.Plot(label="Scheme Weightages")
227
+ final_value = gr.Number(label="Final Value (₹)", interactive=False)
228
+ total_investment = gr.Number(label="Total Investment (₹)", interactive=False)
229
+
230
+ def update_custom_date_visibility(period):
231
+ return {custom_start_date: gr.update(visible=period=="Custom"),
232
+ custom_end_date: gr.update(visible=period=="Custom")}
233
+
234
+ period.change(update_custom_date_visibility, inputs=[period], outputs=[custom_start_date, custom_end_date])
235
+
236
+ scheme_dropdown.key_up(
237
+ fn=update_scheme_dropdown,
238
+ inputs=[scheme_dropdown, gr.State(schemes_df)],
239
+ outputs=scheme_dropdown,
240
+ queue=False,
241
+ show_progress="hidden"
242
+ )
243
+
244
+ add_button.click(add_scheme_to_list,
245
+ inputs=[schemes_list, scheme_dropdown, scheme_weight],
246
+ outputs=[schemes_list, schemes_table, scheme_dropdown, scheme_weight])
247
+
248
+ def update_schemes_and_show_error(schemes_list, updated_data):
249
+ new_schemes_list, updated_table, error = update_schemes(schemes_list, updated_data)
250
+ return (
251
+ new_schemes_list,
252
+ updated_table,
253
+ gr.update(value=error, visible=bool(error))
254
+ )
255
+
256
+ update_button.click(
257
+ update_schemes_and_show_error,
258
+ inputs=[schemes_list, schemes_table],
259
+ outputs=[schemes_list, schemes_table, error_message]
260
+ )
261
+
262
+ schemes_table.select(
263
+ handle_row_selection,
264
+ inputs=[schemes_list, schemes_table],
265
+ outputs=[schemes_table, schemes_list]
266
+ )
267
+ calculate_button.click(
268
+ lambda *args: update_sip_calculator(*prepare_inputs(*args)),
269
+ inputs=[period, custom_start_date, custom_end_date,SIP_Date,sip_amount, schemes_list, gr.State(schemes_df)],
270
+ outputs=[result, pie_chart, final_value, total_investment]
271
+ )
272
+
273
+ return app
274
+
275
+ app = create_ui()
276
+ app.launch()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ gradio
2
+ pandas
3
+ numpy
4
+ plotly
5
+ datetime
6
+ requests