phildunphy commited on
Commit
4171dd0
·
1 Parent(s): eea5819

Included summary based on annualised returns

Browse files
Files changed (1) hide show
  1. app.py +116 -45
app.py CHANGED
@@ -2,23 +2,8 @@ import pandas as pd
2
  import numpy as np
3
  from itertools import combinations
4
  from scipy.optimize import minimize
5
-
6
  import numpy as np
7
- import itertools
8
-
9
-
10
-
11
-
12
-
13
- def filter_top_funds(df, asset_category, n):
14
- category_df = df[(df["asset_category"] == asset_category) & df['morningstar_rating'].isin([4,5])].nlargest(n, "annualized_returns_3yr (%)")
15
- return category_df
16
-
17
- # Function to check if a fund combination has at least two different asset categories
18
- def has_different_asset_categories(fund_combination):
19
- asset_categories = set(fund["asset_category"] for fund in fund_combination)
20
- return len(asset_categories) >= 3
21
-
22
 
23
  def complete_fund_data(fund_combinations):
24
  completed_fund_combinations = []
@@ -38,6 +23,47 @@ def complete_fund_data(fund_combinations):
38
  print (len(completed_fund_combinations))
39
  return completed_fund_combinations
40
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  def find_closest_weights(combination, constraints):
42
  min_difference = float('inf')
43
  best_weights = None
@@ -67,9 +93,7 @@ def end_goal_achieved(yearly_amount, annualized_returns, years):
67
  return yearly_amount * (((1 + annualized_returns)**years - 1) / annualized_returns)
68
 
69
 
70
-
71
-
72
-
73
  def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constraints_equity, constraints_others, end_goal, years):
74
 
75
  stepwise = int(end_goal/1000)
@@ -86,6 +110,9 @@ def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constrain
86
  fund_data_list = []
87
  unique_fund_ids = top_funds["id"].unique()
88
 
 
 
 
89
  for fund_id in unique_fund_ids:
90
  fund_data = {"fund_id": fund_id, "cash": 0, "debt": 0, "equity": 0, "others": 0}
91
  fund_rows = df[df["id"] == fund_id]
@@ -97,11 +124,10 @@ def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constrain
97
 
98
  fund_data_list.append(fund_data)
99
 
100
-
101
-
102
- fund_combinations = list(filter(has_different_asset_categories, combinations(fund_data_list, 3)))
103
 
104
  completed_fund_combinations = complete_fund_data(fund_combinations)
 
105
 
106
  fund_combinations_with_weight_and_difference = []
107
 
@@ -121,9 +147,6 @@ def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constrain
121
  fund_combinations_with_weight_and_difference.sort(key=lambda x: x[1])
122
 
123
 
124
-
125
-
126
-
127
  fund_combinations_with_goal = []
128
 
129
  for fund_combination, difference in fund_combinations_with_weight_and_difference:
@@ -184,36 +207,69 @@ def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constrain
184
  combined_fund_combinations_updated.append(updated_fund_info)
185
 
186
  combined_fund_combinations_updated_df = pd.DataFrame(combined_fund_combinations_updated)
187
- # Create a mapping of fund ids to fund names
 
188
  fund_id_to_name = df.set_index("id")["name"].to_dict()
 
189
 
190
- # Map fund ids to fund names in combined_fund_combinations_updated
191
  for fund_combination_info in combined_fund_combinations_updated:
192
  fund_combination = fund_combination_info["fund_combination"]
193
  for i, fund in enumerate(fund_combination):
194
  fund_name_key = f"fund_{i+1}_name"
 
195
  fund_id = fund["fund_id"]
196
  fund_combination_info[fund_name_key] = fund_id_to_name[fund_id]
 
197
 
198
- # Create a new DataFrame with the updated fund combination info
199
- combined_fund_combinations_updated_df = pd.DataFrame(combined_fund_combinations_updated)
200
- # print(combined_fund_combinations_updated_df)
 
201
 
202
- combined_fund_combinations_updated_df = pd.DataFrame(combined_fund_combinations_updated)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
- combined_fund_combinations_updated_df.columns = ['fund_combination', 'min_deviation_goal (in Rupees)', 'achieved_end_goal',
205
- 'difference', 'overall_constraints', 'weight_1', 'weight_2', 'weight_3',
206
- 'fund_1_name', 'fund_2_name', 'fund_3_name']
207
 
 
 
 
208
 
209
- # Extracting invested_yearly values into separate columns
210
  combined_fund_combinations_updated_df['invested_yearly_fund1'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[0]['invested_yearly'])
211
  combined_fund_combinations_updated_df['invested_yearly_fund2'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[1]['invested_yearly'])
212
  combined_fund_combinations_updated_df['invested_yearly_fund3'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[2]['invested_yearly'])
213
 
 
 
 
 
214
 
215
  # Use the function with your DataFrame:
216
- top_10_rows = combined_fund_combinations_updated_df.head(10)[['min_deviation_goal (in Rupees)',
217
  'achieved_end_goal', 'difference', 'overall_constraints', 'weight_1',
218
  'weight_2', 'weight_3', 'fund_1_name', 'fund_2_name', 'fund_3_name',
219
  'invested_yearly_fund1', 'invested_yearly_fund2',
@@ -226,31 +282,42 @@ def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constrain
226
  rounded_constraints = {key: round(value, 2) for key, value in row['overall_constraints'].items()}
227
  output_str += f"The funds to be invested are {row['weight_1'] * 100:.2f}% in '{row['fund_1_name']}', {row['weight_2'] * 100:.2f}% in '{row['fund_2_name']}', {row['weight_3'] * 100:.2f}% in '{row['fund_3_name']}'.\n"
228
  output_str += f"The constraints found closest to given constraints are {rounded_constraints}.\n"
229
- output_str += f"Amount to be invested is {row['invested_yearly_fund1']:.2f}, {row['invested_yearly_fund2']:.2f}, {row['invested_yearly_fund3']:.2f} and details are min_deviation_goal: {row['min_deviation_goal (in Rupees)']:.2f}, achieved_end_goal: {row['achieved_end_goal']:.2f}, difference: {row['difference']:.2f}.\n\n"
230
 
231
 
232
- return(output_str)
233
 
234
 
235
  import gradio as gr
236
 
237
- # Your code starts here
 
 
 
 
 
 
 
 
 
 
238
 
239
  filename = 'funds_growth.csv'
240
  df = pd.read_csv(filename)
241
 
242
  df_no_dup = df.drop_duplicates(subset=['id'], keep='first')
243
 
 
 
244
 
245
- top_hybrid = filter_top_funds(df_no_dup, "Hybrid", 30)
 
 
246
  top_equity = filter_top_funds(df_no_dup, "Equity", 75)
247
- top_debt = filter_top_funds(df_no_dup, "Debt", 75)
248
 
249
  top_funds = pd.concat([top_hybrid, top_equity, top_debt]).drop_duplicates(subset=['id'], keep='first')
250
 
251
-
252
-
253
-
254
  inputs = [
255
  gr.Slider(0.01, 0.97, value=0.05, label="Constraints Cash (%)"),
256
  gr.Slider(0.01, 0.97, value=0.3, label="Constraints Debt (%)"),
@@ -262,6 +329,11 @@ inputs = [
262
 
263
  output = gr.outputs.Textbox(label="Information Desk",)
264
 
 
 
 
 
 
265
  section4_title = "Reverse Asset Allocation"
266
  section4_desc = "</b></h3> You tell us how much is your risk appetite, we will tell you whats the best 3 fund combo \n </b></h3> "
267
 
@@ -270,4 +342,3 @@ gr.Interface(fn=calcualte_combined_combo_funds, inputs=inputs, outputs=output,
270
  ).launch()
271
 
272
 
273
-
 
2
  import numpy as np
3
  from itertools import combinations
4
  from scipy.optimize import minimize
 
5
  import numpy as np
6
+ from itertools import combinations as itertools_combinations
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  def complete_fund_data(fund_combinations):
9
  completed_fund_combinations = []
 
23
  print (len(completed_fund_combinations))
24
  return completed_fund_combinations
25
 
26
+
27
+ def has_different_asset_categories(constraints, fund_combination):
28
+ equity_target = constraints['equity']
29
+
30
+ def count_funds_by_category(category):
31
+ return sum(1 for fund in fund_combination if fund["asset_category"] == category)
32
+
33
+ if equity_target > 0.65:
34
+ num_equity = count_funds_by_category("Equity")
35
+ num_hybrid = count_funds_by_category("Hybrid")
36
+ return (
37
+ all(fund["asset_category"] != "Debt" for fund in fund_combination)
38
+ and num_hybrid >= 1
39
+ and num_equity < 3
40
+ )
41
+ elif 0.5 <= equity_target <= 0.65:
42
+ num_debt = count_funds_by_category("Debt")
43
+ num_equity = count_funds_by_category("Equity")
44
+ num_hybrid = count_funds_by_category("Hybrid")
45
+ return (
46
+ num_debt == 1
47
+ and num_equity == 2
48
+ and num_hybrid == 0
49
+ )
50
+ else:
51
+ num_debt = count_funds_by_category("Debt")
52
+ num_equity = count_funds_by_category("Equity")
53
+ num_hybrid = count_funds_by_category("Hybrid")
54
+ return (
55
+ num_debt == 2
56
+ and num_equity == 1
57
+ and num_hybrid == 0
58
+ )
59
+
60
+
61
+ def filter_top_funds(df, asset_category, n):
62
+ category_df = df[(df["asset_category"] == asset_category) & df['morningstar_rating'].isin([4,5])].nlargest(n, "annualized_returns_3yr (%)")
63
+ return category_df
64
+
65
+
66
+
67
  def find_closest_weights(combination, constraints):
68
  min_difference = float('inf')
69
  best_weights = None
 
93
  return yearly_amount * (((1 + annualized_returns)**years - 1) / annualized_returns)
94
 
95
 
96
+
 
 
97
  def calcualte_combined_combo_funds(constraints_cash, constraints_debt, constraints_equity, constraints_others, end_goal, years):
98
 
99
  stepwise = int(end_goal/1000)
 
110
  fund_data_list = []
111
  unique_fund_ids = top_funds["id"].unique()
112
 
113
+ fund_data_list = []
114
+ unique_fund_ids = top_funds["id"].unique()
115
+
116
  for fund_id in unique_fund_ids:
117
  fund_data = {"fund_id": fund_id, "cash": 0, "debt": 0, "equity": 0, "others": 0}
118
  fund_rows = df[df["id"] == fund_id]
 
124
 
125
  fund_data_list.append(fund_data)
126
 
127
+ fund_combinations = list(filter(lambda x: has_different_asset_categories(constraints, x), combinations(fund_data_list, 3)))
 
 
128
 
129
  completed_fund_combinations = complete_fund_data(fund_combinations)
130
+
131
 
132
  fund_combinations_with_weight_and_difference = []
133
 
 
147
  fund_combinations_with_weight_and_difference.sort(key=lambda x: x[1])
148
 
149
 
 
 
 
150
  fund_combinations_with_goal = []
151
 
152
  for fund_combination, difference in fund_combinations_with_weight_and_difference:
 
207
  combined_fund_combinations_updated.append(updated_fund_info)
208
 
209
  combined_fund_combinations_updated_df = pd.DataFrame(combined_fund_combinations_updated)
210
+
211
+ # Create a mapping of fund ids to fund names and annualized_returns_3yr
212
  fund_id_to_name = df.set_index("id")["name"].to_dict()
213
+ fund_id_to_annualized_returns_3yr = df.set_index("id")["annualized_returns_3yr (%)"].to_dict()
214
 
215
+ # Map fund ids to fund names and annualized_returns_3yr in combined_fund_combinations_updated
216
  for fund_combination_info in combined_fund_combinations_updated:
217
  fund_combination = fund_combination_info["fund_combination"]
218
  for i, fund in enumerate(fund_combination):
219
  fund_name_key = f"fund_{i+1}_name"
220
+ fund_returns_key = f"fund_{i+1}_annualized_returns_3yr"
221
  fund_id = fund["fund_id"]
222
  fund_combination_info[fund_name_key] = fund_id_to_name[fund_id]
223
+ fund_combination_info[fund_returns_key] = fund_id_to_annualized_returns_3yr[fund_id]
224
 
225
+ # Add the new columns to the DataFrame
226
+ combined_fund_combinations_updated_df['fund_1_annualized_returns_3yr'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[0]['annualized_returns_3yr'])
227
+ combined_fund_combinations_updated_df['fund_2_annualized_returns_3yr'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[1]['annualized_returns_3yr'])
228
+ combined_fund_combinations_updated_df['fund_3_annualized_returns_3yr'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[2]['annualized_returns_3yr'])
229
 
230
+ fund_id_to_name = dict(zip(df_no_dup['id'], df_no_dup['name']))
231
+
232
+
233
+ # Sort the DataFrame based on the sum of annualized_returns_3yr for each fund combination and constraints deviation
234
+ combined_fund_combinations_updated_df['total_annualized_returns_3yr'] = combined_fund_combinations_updated_df['fund_1_annualized_returns_3yr'] + combined_fund_combinations_updated_df['fund_2_annualized_returns_3yr'] + combined_fund_combinations_updated_df['fund_3_annualized_returns_3yr']
235
+
236
+ combined_fund_combinations_updated_df['fund_combination'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: add_fund_name(x, fund_id_to_name))
237
+
238
+ # Sort by both annualized returns and constraints deviation
239
+ sorted_df = combined_fund_combinations_updated_df.sort_values(by=['total_annualized_returns_3yr', 'min_deviation_goal'], ascending=[False, True])
240
+
241
+
242
+ #Select the top 10 rows
243
+ top_30_rows_annual_return_sorted = sorted_df[sorted_df['difference']<0.1].head(10)[['difference', 'achieved_end_goal', 'overall_constraints', 'weight_1', 'weight_2', 'weight_3', 'min_deviation_goal']]
244
+ for i in range(1, 4):
245
+ top_30_rows_annual_return_sorted[f'fund_{i}_name'] = sorted_df[sorted_df['difference']<0.2]['fund_combination'].apply(lambda x: x[i-1]['name'])
246
+ top_30_rows_annual_return_sorted[f'invested_yearly_fund{i}'] = sorted_df[sorted_df['difference']<0.2]['fund_combination'].apply(lambda x: x[i-1]['invested_yearly'])
247
+ top_30_rows_annual_return_sorted[f'fund_{i}_annualized_returns_3yr'] = sorted_df[sorted_df['difference']<0.2]['fund_combination'].apply(lambda x: x[i-1]['annualized_returns_3yr'])
248
+
249
+ # Create the output string
250
+ output_str_annual_return_sorted = ""
251
+ for index, row in top_30_rows_annual_return_sorted.iterrows():
252
+ rounded_constraints = {key: round(value, 2) for key, value in row['overall_constraints'].items()}
253
+ output_str_annual_return_sorted += f"The funds to be invested are {row['weight_1'] * 100:.2f}% in '{row['fund_1_name']}', {row['weight_2'] * 100:.2f}% in '{row['fund_2_name']}', {row['weight_3'] * 100:.2f}% in '{row['fund_3_name']}'.\n"
254
+ output_str_annual_return_sorted += f"The constraints found closest to given constraints are {rounded_constraints}.\n"
255
+ output_str_annual_return_sorted += f"Amount to be invested is {row['invested_yearly_fund1']:.2f}, {row['invested_yearly_fund2']:.2f}, {row['invested_yearly_fund3']:.2f} and details are min_deviation_goal: {row['min_deviation_goal']:.2f}, achieved_end_goal: {row['achieved_end_goal']:.2f}, difference: {row['difference']:.2f}.\n\n"
256
 
 
 
 
257
 
258
+ combined_fund_combinations_updated_df['fund_1_name'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[0]['name'])
259
+ combined_fund_combinations_updated_df['fund_2_name'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[1]['name'])
260
+ combined_fund_combinations_updated_df['fund_3_name'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[2]['name'])
261
 
 
262
  combined_fund_combinations_updated_df['invested_yearly_fund1'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[0]['invested_yearly'])
263
  combined_fund_combinations_updated_df['invested_yearly_fund2'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[1]['invested_yearly'])
264
  combined_fund_combinations_updated_df['invested_yearly_fund3'] = combined_fund_combinations_updated_df['fund_combination'].apply(lambda x: x[2]['invested_yearly'])
265
 
266
+ combined_fund_combinations_updated_df = combined_fund_combinations_updated_df.sort_values(by=['difference'],ascending=[True])
267
+ # Extract the required columns in top_10_rows
268
+ top_10_rows = combined_fund_combinations_updated_df.head(10)[['min_deviation_goal', 'achieved_end_goal', 'difference', 'overall_constraints', 'weight_1', 'weight_2', 'weight_3', 'fund_1_name', 'fund_2_name', 'fund_3_name', 'invested_yearly_fund1', 'invested_yearly_fund2', 'invested_yearly_fund3', 'fund_1_annualized_returns_3yr', 'fund_2_annualized_returns_3yr', 'fund_3_annualized_returns_3yr']]
269
+
270
 
271
  # Use the function with your DataFrame:
272
+ top_10_rows = combined_fund_combinations_updated_df.head(25)[['min_deviation_goal',
273
  'achieved_end_goal', 'difference', 'overall_constraints', 'weight_1',
274
  'weight_2', 'weight_3', 'fund_1_name', 'fund_2_name', 'fund_3_name',
275
  'invested_yearly_fund1', 'invested_yearly_fund2',
 
282
  rounded_constraints = {key: round(value, 2) for key, value in row['overall_constraints'].items()}
283
  output_str += f"The funds to be invested are {row['weight_1'] * 100:.2f}% in '{row['fund_1_name']}', {row['weight_2'] * 100:.2f}% in '{row['fund_2_name']}', {row['weight_3'] * 100:.2f}% in '{row['fund_3_name']}'.\n"
284
  output_str += f"The constraints found closest to given constraints are {rounded_constraints}.\n"
285
+ output_str += f"Amount to be invested is {row['invested_yearly_fund1']:.2f}, {row['invested_yearly_fund2']:.2f}, {row['invested_yearly_fund3']:.2f} and details are min_deviation_goal: {row['min_deviation_goal']:.2f}, achieved_end_goal: {row['achieved_end_goal']:.2f}, difference: {row['difference']:.2f}.\n\n"
286
 
287
 
288
+ return(output_str,output_str_annual_return_sorted)
289
 
290
 
291
  import gradio as gr
292
 
293
+ import pandas as pd
294
+ from itertools import combinations
295
+
296
+
297
+
298
+ constraints = {
299
+ 'cash': 0.03,
300
+ 'debt': 0.3,
301
+ 'equity': 0.67,
302
+ 'others': 0.01,
303
+ }
304
 
305
  filename = 'funds_growth.csv'
306
  df = pd.read_csv(filename)
307
 
308
  df_no_dup = df.drop_duplicates(subset=['id'], keep='first')
309
 
310
+ hybrid_check = df[(df['asset_category']=='Hybrid') & (df['holding_type']=='equity')]
311
+ hybrid_check_filter = hybrid_check[hybrid_check['weightage']>65]
312
 
313
+
314
+ top_hybrid_raw = filter_top_funds(df_no_dup, "Hybrid",100)
315
+ top_hybrid = top_hybrid_raw[top_hybrid_raw['id'].isin(hybrid_check_filter.id)]
316
  top_equity = filter_top_funds(df_no_dup, "Equity", 75)
317
+ top_debt = filter_top_funds(df_no_dup, "Debt", 50)
318
 
319
  top_funds = pd.concat([top_hybrid, top_equity, top_debt]).drop_duplicates(subset=['id'], keep='first')
320
 
 
 
 
321
  inputs = [
322
  gr.Slider(0.01, 0.97, value=0.05, label="Constraints Cash (%)"),
323
  gr.Slider(0.01, 0.97, value=0.3, label="Constraints Debt (%)"),
 
329
 
330
  output = gr.outputs.Textbox(label="Information Desk",)
331
 
332
+ output = [
333
+ gr.outputs.Textbox(label="Summary (sorted on closest constraint)"),
334
+ gr.outputs.Textbox(label="Summary (sorted on 3yr returns)"),
335
+ ]
336
+
337
  section4_title = "Reverse Asset Allocation"
338
  section4_desc = "</b></h3> You tell us how much is your risk appetite, we will tell you whats the best 3 fund combo \n </b></h3> "
339
 
 
342
  ).launch()
343
 
344