vibhorag101 commited on
Commit
57f491f
1 Parent(s): 0530756

Added inception date toggle

Browse files
Files changed (1) hide show
  1. app.py +81 -32
app.py CHANGED
@@ -4,7 +4,6 @@ import plotly.graph_objects as go
4
  from datetime import datetime, timedelta
5
  import requests
6
 
7
-
8
  js_func = """
9
  function refresh() {
10
  const url = new URL(window.location);
@@ -24,9 +23,10 @@ def get_nav_data(scheme_code):
24
  df['date'] = pd.to_datetime(df['date'], format='%d-%m-%Y')
25
  df['nav'] = df['nav'].astype(float)
26
  df = df.sort_values('date')
27
- return df
 
28
 
29
- def calculate_sip_returns(nav_data, sip_amount, start_date, end_date,SIP_Date):
30
  start_date = pd.Timestamp(start_date)
31
  end_date = pd.Timestamp(end_date)
32
 
@@ -60,21 +60,23 @@ def create_pie_chart(schemes):
60
  fig.update_layout(title_text="Scheme Weightages")
61
  return fig
62
 
63
- def calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_date,schemes_df):
64
  scheme_returns = []
65
  total_investment = 0
66
  final_value = 0
 
67
 
68
  for scheme_name, scheme_weight in schemes.items():
69
  scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
70
- nav_data = get_nav_data(scheme_code)
71
- scheme_return, scheme_final_value, scheme_total_investment = calculate_sip_returns(nav_data, sip_amount * scheme_weight / 100, start_date, end_date,SIP_date)
 
72
  scheme_returns.append((scheme_name, scheme_return))
73
  final_value += scheme_final_value
74
  total_investment += scheme_total_investment
75
 
76
  portfolio_return = (final_value - total_investment) / total_investment * 100
77
- return portfolio_return, final_value, total_investment, scheme_returns
78
 
79
  def update_sip_calculator(*args):
80
  period = args[0]
@@ -85,10 +87,12 @@ def update_sip_calculator(*args):
85
  schemes_df = args[5]
86
  schemes = {}
87
 
88
- for i in range(6, len(args), 2):
89
  if args[i] and args[i+1]:
90
  schemes[args[i]] = float(args[i+1])
91
 
 
 
92
  if not schemes:
93
  return "Please add at least one scheme.", None, None, None
94
 
@@ -96,30 +100,48 @@ def update_sip_calculator(*args):
96
 
97
  end_date = datetime.now().date()
98
 
99
- if period == "Custom":
 
 
100
  if not custom_start_date or not custom_end_date:
101
  return "Please provide both start and end dates for custom period.", None, None, None
102
  start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
103
  end_date = datetime.strptime(custom_end_date, "%Y-%m-%d").date()
104
-
105
  elif period == "YTD":
106
- start_date = datetime(end_date.year, 1, 1)
107
-
 
108
  else:
109
- # check if string contaiins year
110
- if 'year' in period.split()[1]:
111
- years = int(period.split()[0])
 
 
 
112
  start_date = end_date - timedelta(days=years*365)
113
  else:
114
- months = int(period.split()[0])
115
  start_date = end_date - timedelta(days=months*30)
116
 
117
  try:
118
- portfolio_return, final_value, total_investment, scheme_returns = calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_Date,schemes_df)
119
  except Exception as e:
120
  return f"Error: {str(e)}", None, None, None
121
 
122
- result = f"Total portfolio SIP return: {portfolio_return:.2f}%\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
123
  result += f"Total investment: ₹{total_investment:.2f}\n"
124
  result += f"Final value: ₹{final_value:.2f}\n\n"
125
  result += "Individual scheme returns:\n"
@@ -182,17 +204,13 @@ def update_schemes(schemes_list, updated_data):
182
  error_msg = f"Error updating schemes: {str(e)}"
183
  return schemes_list, update_schemes_table(schemes_list), error_msg
184
 
185
- def prepare_inputs(period, custom_start, custom_end,SIP_Date,sip_amount, schemes_list, schemes_df,):
186
- inputs = [period, custom_start, custom_end,SIP_Date, sip_amount, schemes_df]
187
  for name, weight in schemes_list:
188
  inputs.extend([name, weight])
189
  return inputs
190
 
191
  def handle_row_selection(schemes_list, evt: gr.SelectData, table_data):
192
- # print(f"Event data: {evt}")
193
- # print(f"Event index: {evt.index}")
194
- # print(f"Table data: {table_data}")
195
-
196
  if evt.index is not None and len(evt.index) > 1:
197
  column_index = evt.index[1]
198
  if column_index == 2: # "Actions" column
@@ -204,11 +222,6 @@ def handle_row_selection(schemes_list, evt: gr.SelectData, table_data):
204
  return table_data, updated_schemes_list
205
  return table_data, schemes_list
206
 
207
- def update_schemes_table(schemes_list):
208
- df = pd.DataFrame(schemes_list, columns=["Scheme Name", "Weight (%)"])
209
- df["Actions"] = "❌"
210
- return df
211
-
212
  def create_ui():
213
  schemes_df = fetch_scheme_data()
214
 
@@ -220,6 +233,9 @@ def create_ui():
220
  custom_start_date = gr.Textbox(label="Custom Start Date (YYYY-MM-DD)", visible=False)
221
  custom_end_date = gr.Textbox(label="Custom End Date (YYYY-MM-DD)", visible=False)
222
  SIP_Date = gr.Dropdown(label="Monthly SIP Date", choices=["start","middle","end"])
 
 
 
223
 
224
  sip_amount = gr.Number(label="SIP Amount (₹)")
225
 
@@ -241,7 +257,7 @@ def create_ui():
241
 
242
  update_button = gr.Button("Update Schemes")
243
  error_message = gr.Textbox(label="Error", visible=False)
244
-
245
  calculate_button = gr.Button("Calculate Returns")
246
 
247
  result = gr.Textbox(label="Results")
@@ -286,9 +302,42 @@ def create_ui():
286
  inputs=[schemes_list, schemes_table],
287
  outputs=[schemes_table, schemes_list]
288
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
  calculate_button.click(
290
- lambda *args: update_sip_calculator(*prepare_inputs(*args)),
291
- inputs=[period, custom_start_date, custom_end_date,SIP_Date,sip_amount, schemes_list, gr.State(schemes_df)],
292
  outputs=[result, pie_chart, final_value, total_investment]
293
  )
294
 
 
4
  from datetime import datetime, timedelta
5
  import requests
6
 
 
7
  js_func = """
8
  function refresh() {
9
  const url = new URL(window.location);
 
23
  df['date'] = pd.to_datetime(df['date'], format='%d-%m-%Y')
24
  df['nav'] = df['nav'].astype(float)
25
  df = df.sort_values('date')
26
+ inception_date = df['date'].min()
27
+ return df, inception_date
28
 
29
+ def calculate_sip_returns(nav_data, sip_amount, start_date, end_date, SIP_Date):
30
  start_date = pd.Timestamp(start_date)
31
  end_date = pd.Timestamp(end_date)
32
 
 
60
  fig.update_layout(title_text="Scheme Weightages")
61
  return fig
62
 
63
+ def calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_date, schemes_df):
64
  scheme_returns = []
65
  total_investment = 0
66
  final_value = 0
67
+ inception_dates = []
68
 
69
  for scheme_name, scheme_weight in schemes.items():
70
  scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
71
+ nav_data, inception_date = get_nav_data(scheme_code)
72
+ inception_dates.append((scheme_name, inception_date))
73
+ scheme_return, scheme_final_value, scheme_total_investment = calculate_sip_returns(nav_data, sip_amount * scheme_weight / 100, start_date, end_date, SIP_date)
74
  scheme_returns.append((scheme_name, scheme_return))
75
  final_value += scheme_final_value
76
  total_investment += scheme_total_investment
77
 
78
  portfolio_return = (final_value - total_investment) / total_investment * 100
79
+ return portfolio_return, final_value, total_investment, scheme_returns, inception_dates
80
 
81
  def update_sip_calculator(*args):
82
  period = args[0]
 
87
  schemes_df = args[5]
88
  schemes = {}
89
 
90
+ for i in range(6, len(args) - 1, 2): # Adjust range to account for use_inception_date
91
  if args[i] and args[i+1]:
92
  schemes[args[i]] = float(args[i+1])
93
 
94
+ use_inception_date = args[-1] # Get use_inception_date from the last argument
95
+
96
  if not schemes:
97
  return "Please add at least one scheme.", None, None, None
98
 
 
100
 
101
  end_date = datetime.now().date()
102
 
103
+ if use_inception_date:
104
+ start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
105
+ elif period == "Custom":
106
  if not custom_start_date or not custom_end_date:
107
  return "Please provide both start and end dates for custom period.", None, None, None
108
  start_date = datetime.strptime(custom_start_date, "%Y-%m-%d").date()
109
  end_date = datetime.strptime(custom_end_date, "%Y-%m-%d").date()
 
110
  elif period == "YTD":
111
+ start_date = datetime(end_date.year, 1, 1).date()
112
+ elif not period:
113
+ return "Please select a period, provide custom dates, or use the inception date.", None, None, None
114
  else:
115
+ period_parts = period.split()
116
+ if len(period_parts) < 2:
117
+ return "Invalid period selected.", None, None, None
118
+
119
+ if 'year' in period_parts[1]:
120
+ years = int(period_parts[0])
121
  start_date = end_date - timedelta(days=years*365)
122
  else:
123
+ months = int(period_parts[0])
124
  start_date = end_date - timedelta(days=months*30)
125
 
126
  try:
127
+ portfolio_return, final_value, total_investment, scheme_returns, inception_dates = calculate_portfolio_returns(schemes, sip_amount, start_date, end_date, SIP_Date, schemes_df)
128
  except Exception as e:
129
  return f"Error: {str(e)}", None, None, None
130
 
131
+ # Check if start_date is before any scheme's inception date
132
+ inception_warnings = []
133
+ earliest_inception_date = min(inception_date for _, inception_date in inception_dates)
134
+ for scheme_name, inception_date in inception_dates:
135
+ if start_date < inception_date.date():
136
+ inception_warnings.append(f"Warning: {scheme_name} inception date ({inception_date.date()}) is after the chosen start date ({start_date}).")
137
+
138
+ result = ""
139
+ if inception_warnings:
140
+ result += "The following warnings were found:\n"
141
+ result += "\n".join(inception_warnings) + "\n\n"
142
+ result += f"The earliest possible start date for all chosen schemes is: {earliest_inception_date.date()}\n\n"
143
+
144
+ result += f"Total portfolio SIP return: {portfolio_return:.2f}%\n"
145
  result += f"Total investment: ₹{total_investment:.2f}\n"
146
  result += f"Final value: ₹{final_value:.2f}\n\n"
147
  result += "Individual scheme returns:\n"
 
204
  error_msg = f"Error updating schemes: {str(e)}"
205
  return schemes_list, update_schemes_table(schemes_list), error_msg
206
 
207
+ def prepare_inputs(period, custom_start, custom_end, SIP_Date, sip_amount, schemes_list, schemes_df):
208
+ inputs = [period, custom_start, custom_end, SIP_Date, sip_amount, schemes_df]
209
  for name, weight in schemes_list:
210
  inputs.extend([name, weight])
211
  return inputs
212
 
213
  def handle_row_selection(schemes_list, evt: gr.SelectData, table_data):
 
 
 
 
214
  if evt.index is not None and len(evt.index) > 1:
215
  column_index = evt.index[1]
216
  if column_index == 2: # "Actions" column
 
222
  return table_data, updated_schemes_list
223
  return table_data, schemes_list
224
 
 
 
 
 
 
225
  def create_ui():
226
  schemes_df = fetch_scheme_data()
227
 
 
233
  custom_start_date = gr.Textbox(label="Custom Start Date (YYYY-MM-DD)", visible=False)
234
  custom_end_date = gr.Textbox(label="Custom End Date (YYYY-MM-DD)", visible=False)
235
  SIP_Date = gr.Dropdown(label="Monthly SIP Date", choices=["start","middle","end"])
236
+ with gr.Column():
237
+ use_inception_date = gr.Checkbox(label="Use Earliest Inception Date", value=False)
238
+ inception_date_display = gr.Textbox(label="Earliest Inception Date", interactive=False)
239
 
240
  sip_amount = gr.Number(label="SIP Amount (₹)")
241
 
 
257
 
258
  update_button = gr.Button("Update Schemes")
259
  error_message = gr.Textbox(label="Error", visible=False)
260
+
261
  calculate_button = gr.Button("Calculate Returns")
262
 
263
  result = gr.Textbox(label="Results")
 
302
  inputs=[schemes_list, schemes_table],
303
  outputs=[schemes_table, schemes_list]
304
  )
305
+
306
+ def get_earliest_inception_date(schemes_list, schemes_df):
307
+ inception_dates = []
308
+ for scheme_name, _ in schemes_list:
309
+ scheme_code = schemes_df[schemes_df['schemeName'] == scheme_name]['schemeCode'].values[0]
310
+ _, inception_date = get_nav_data(scheme_code)
311
+ inception_dates.append(inception_date)
312
+ return max(inception_dates).strftime("%Y-%m-%d") if inception_dates else ""
313
+
314
+ def update_inception_date(use_inception_date, schemes_list, schemes_df):
315
+ if use_inception_date and schemes_list:
316
+ earliest_inception_date = get_earliest_inception_date(schemes_list, schemes_df)
317
+ return gr.update(value=earliest_inception_date, visible=True)
318
+ else:
319
+ return gr.update(value="", visible=False)
320
+
321
+ use_inception_date.change(
322
+ update_inception_date,
323
+ inputs=[use_inception_date, schemes_list, gr.State(schemes_df)],
324
+ outputs=inception_date_display
325
+ )
326
+
327
+ def prepare_inputs_with_inception(period, custom_start, custom_end, SIP_Date, sip_amount, schemes_list, schemes_df, use_inception_date, inception_date_display):
328
+ inputs = [period, custom_start, custom_end, SIP_Date, sip_amount, schemes_df]
329
+ for name, weight in schemes_list:
330
+ inputs.extend([name, weight])
331
+
332
+ inputs.append(use_inception_date) # Add use_inception_date to the inputs
333
+ if use_inception_date and inception_date_display:
334
+ inputs[1] = inception_date_display # Replace custom_start with inception_date_display
335
+
336
+ return inputs
337
+
338
  calculate_button.click(
339
+ lambda *args: update_sip_calculator(*prepare_inputs_with_inception(*args)),
340
+ inputs=[period, custom_start_date, custom_end_date, SIP_Date, sip_amount, schemes_list, gr.State(schemes_df), use_inception_date, inception_date_display],
341
  outputs=[result, pie_chart, final_value, total_investment]
342
  )
343