File size: 17,587 Bytes
3c4e575
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
import datetime
import math

# --- Helper Functions (mostly unchanged) ---
def get_float(prompt):
    """Helper to get a non-negative float from the user."""
    while True:
        try:
            val_str = input(prompt).strip()
            if not val_str:
                raise ValueError("Input cannot be empty.")
            val = float(val_str)
            if val < 0:
                raise ValueError("Please enter a non-negative number.")
            return val
        except ValueError as e:
            print(f"Invalid input: {e}")

def get_int(prompt):
    """Helper to get a non-negative integer from the user."""
    while True:
        try:
            val_str = input(prompt).strip()
            if not val_str:
                raise ValueError("Input cannot be empty.")
            val = int(val_str)
            if val < 0:
                raise ValueError("Please enter a non-negative whole number.")
            return val
        except ValueError as e:
            print(f"Invalid input: {e}")

def yes_no(prompt):
    """Helper to get a yes/no response."""
    while True:
        resp = input(prompt + " (y/n): ").strip().lower()
        if resp in ("y", "yes"):
            return True
        if resp in ("n", "no"):
            return False
        print("Please answer 'y' or 'n'.")

# --- Simplified Profile and Financials Gathering ---

def get_user_profile():
    """Gathers lifecycle stage, risk appetite, and investment horizon."""
    print("\n--- Let's Understand Your Investor Profile ---")

    # 1. Lifecycle Stage
    print("\nWhich of these best describes your current life stage?")
    lifecycle_options = {
        "1": "Student (Focus: Learning, initial savings)",
        "2": "Early Career (20s-early 30s, Focus: Growth, accumulation)",
        "3": "Mid-Career (Mid 30s-late 40s, Focus: Balancing growth & responsibilities)",
        "4": "Late Career/Pre-Retirement (50s+, Focus: Capital preservation, income generation)",
        "5": "Retired (Focus: Sustainable income, capital preservation)"
    }
    for key, value in lifecycle_options.items():
        print(f"{key}. {value}")
    while True:
        choice = input("Enter number (1-5): ").strip()
        if choice in lifecycle_options:
            lifecycle_stage = lifecycle_options[choice].split('(')[0].strip() # Get "Student", "Early Career" etc.
            break
        else:
            print("Invalid choice. Please enter a number between 1 and 5.")

    # 2. Risk Appetite
    print("\nHow would you describe your risk tolerance for investments?")
    risk_options = {
        "1": "Low (Prefer safety and capital preservation, okay with lower returns)",
        "2": "Medium (Willing to take some risk for moderate growth, comfortable with some fluctuations)",
        "3": "High (Willing to take significant risk for potentially high returns, can handle large fluctuations)"
    }
    for key, value in risk_options.items():
        print(f"{key}. {value}")
    while True:
        choice = input("Enter number (1-3): ").strip()
        if choice == "1": risk_appetite = "low"; break
        elif choice == "2": risk_appetite = "medium"; break
        elif choice == "3": risk_appetite = "high"; break
        else:
            print("Invalid choice. Please enter 1, 2, or 3.")

    # 3. Investment Horizon
    print("\nWhat is your general investment time horizon for the majority of your savings?")
    horizon_options = {
        "1": "Short-term (Less than 3 years)",
        "2": "Medium-term (3-7 years)",
        "3": "Long-term (More than 7 years)"
    }
    for key, value in horizon_options.items():
        print(f"{key}. {value}")
    while True:
        choice = input("Enter number (1-3): ").strip()
        if choice == "1": investment_horizon = "short-term"; break
        elif choice == "2": investment_horizon = "medium-term"; break
        elif choice == "3": investment_horizon = "long-term"; break
        else:
            print("Invalid choice. Please enter 1, 2, or 3.")

    return lifecycle_stage, risk_appetite, investment_horizon

def get_financial_details():
    """Gets annual package, monthly in-hand salary, and total monthly expenses."""
    print("\n--- Let's Get Your Financial Details ---")
    annual_package = get_float("What is your approximate annual salary package (CTC)? \u20B9 ")

    # Simplified monthly in-hand: Ask directly or provide a rough estimate and ask for correction
    print(f"\nBased on an annual package of \u20B9 {annual_package:.2f}, your monthly in-hand salary might be around \u20B9 {annual_package / 14:.2f} to \u20B9 {annual_package / 13:.2f} (this is a rough estimate).")
    monthly_in_hand_salary = get_float("What is your actual average monthly in-hand salary? \u20B9 ")

    total_monthly_expenses = get_float("What are your total estimated monthly expenses (all inclusive)? \u20B9 ")

    monthly_savings = monthly_in_hand_salary - total_monthly_expenses

    print(f"\nBased on your input:")
    print(f"  Monthly In-hand Salary: \u20B9 {monthly_in_hand_salary:.2f}")
    print(f"  Total Monthly Expenses: \u20B9 {total_monthly_expenses:.2f}")
    if monthly_savings > 0:
        print(f"  Calculated Monthly Savings: \u20B9 {monthly_savings:.2f}")
    else:
        print(f"  Calculated Monthly Deficit: \u20B9 {abs(monthly_savings):.2f}")
        print("  It seems your expenses exceed your income. Please review your budget.")

    return monthly_in_hand_salary, total_monthly_expenses, monthly_savings

# --- Simplified Investment Allocation Logic ---
def allocate_savings_simplified(savings, risk_profile, horizon, lifecycle):
    """

    Determines investment allocation percentages based on simplified profile.

    Returns a dictionary of { "Asset Class": percentage, ... }

    """
    allocations = {}
    justification_points = [
        f"This allocation considers your {risk_profile} risk profile, {horizon} horizon, and {lifecycle} stage."
    ]

    # Prioritize Emergency Fund if not explicitly covered or if savings are first-time
    # For simplicity, this version assumes user will manage EF separately or this is for surplus post-EF.
    # A more robust version would inquire about EF status.

    if risk_profile == "low":
        allocations = {
            "Fixed Deposits / Recurring Deposits (Safety & Stability)": 0.35,
            "Debt Mutual Funds (Liquid/Short Duration - Better than savings account returns)": 0.30,
            "Gold (SGBs/ETFs - Inflation Hedge, Diversification)": 0.10,
            "Equity MFs (Large Cap/Index Funds - Long-term inflation beating, low volatility equity)": 0.15,
            "Cash / Savings Account (Immediate Liquidity)": 0.10
        }
        justification_points.append("- Focus on capital preservation and stable returns.")
        justification_points.append("- Suitable for short-term goals or very conservative investors.")
        if horizon != "short-term":
            justification_points.append("- Even with a longer horizon, a 'low' risk choice emphasizes safety above all.")


    elif risk_profile == "medium":
        allocations = {
            "Equity MFs (Diversified - Large & Mid Cap/Flexi Cap SIPs for growth)": 0.45,
            "Debt Mutual Funds (For stability and portfolio balance)": 0.25,
            "Fixed Deposits / PPF (Secure, long-term component)": 0.10,
            "Gold (SGBs/ETFs - Diversification)": 0.10,
            "Equity MFs (International - For global diversification, if comfortable)": 0.05, # Optional
            "Cash / Savings Account (Buffer)": 0.05
        }
        justification_points.append("- Aims for a balance between growth (equity) and stability (debt, gold).")
        justification_points.append("- Suitable for medium to long-term goals.")
        if lifecycle in ["Early Career", "Student"] and horizon == "long-term":
            justification_points.append("- Your stage and horizon allow for good equity exposure for wealth creation.")
            # Could slightly increase equity for very young, long-term, medium risk.
            # allocations["Equity MFs (Diversified - Large & Mid Cap/Flexi Cap SIPs for growth)"] = 0.50
            # allocations["Debt Mutual Funds (For stability and portfolio balance)"] = 0.20

    elif risk_profile == "high":
        allocations = {
            "Equity MFs (Aggressive Growth - Mid/Small Cap, Thematic SIPs, with research)": 0.60,
            "Equity MFs (International - Global growth opportunities)": 0.15,
            "Direct Stocks (If experienced & well-researched, otherwise add to Equity MFs)": 0.10, # User needs to self-assess this.
            "Debt Mutual Funds (Strategic, for some diversification)": 0.05,
            "Gold (SGBs/ETFs - Tactical Diversification)": 0.05,
            "Alternative Inv. (REITs/InvITs - very small, if understood & suitable, else to Equity)": 0.05
        }
        justification_points.append("- Focuses on maximizing long-term growth potential, accepting higher volatility.")
        justification_points.append("- Best suited for long-term goals and investors comfortable with significant market swings.")
        if lifecycle not in ["Early Career", "Mid-Career"] and horizon == "long-term":
             justification_points.append(f"- CAUTION: High risk at {lifecycle} stage needs careful consideration of your overall financial stability and nearness to needing funds.")
        if horizon != "long-term":
            justification_points.append(f"- CAUTION: High risk for a {horizon} horizon is generally not advisable. Ensure goals truly allow for this risk.")


    # Normalize percentages to ensure they sum to 100%
    current_total_percentage = sum(allocations.values())
    if abs(current_total_percentage - 1.0) > 0.001: # If not already 100%
        factor = 1.0 / current_total_percentage
        normalized_allocations = {k: v * factor for k, v in allocations.items()}
        # Small check to ensure the largest component doesn't become negative if factor is weird (shouldn't happen with positive inputs)
        # And ensure no tiny values are left that make no sense, e.g. less than 0.5% could be merged.
        # For simplicity, we'll assume initial definitions are close enough.
        final_allocations = {}
        temp_sum = 0
        for asset, perc in normalized_allocations.items():
            # Round to sensible points e.g. 1 decimal for percentage display
            # but use more precision for calculation
            final_allocations[asset] = perc
            temp_sum += perc

        # Final check and adjustment of the largest item if sum isn't perfect due to rounding during normalization.
        if abs(temp_sum - 1.0) > 0.0001:
            diff = 1.0 - temp_sum
            if final_allocations: # Check if dict is not empty
                 # Find largest item to adjust
                largest_item_key = max(final_allocations, key=final_allocations.get)
                final_allocations[largest_item_key] += diff
        allocations = final_allocations


    return allocations, justification_points

def display_investment_plan(monthly_savings, allocations, justification):
    """Displays the final investment plan and justification."""
    print("\n\n--- Your Personalized Investment Allocation Plan ---")
    print(f"Based on your monthly savings of \u20B9 {monthly_savings:.2f}:\n")

    print("Suggested Allocation:")
    print("---------------------------------------------------------------------------")
    print(f"{'Asset Class/Instrument':<50} | {'Percentage':>12} | {'Amount (β‚Ή)':>12}")
    print("---------------------------------------------------------------------------")
    if not allocations: # Should not happen if logic is correct
        print("No specific allocation generated. Please review inputs or consult an advisor.")
        return

    total_allocated_amount_check = 0
    for asset, percentage in allocations.items():
        amount = monthly_savings * percentage
        total_allocated_amount_check += amount
        print(f"{asset:<50} | {percentage*100:>11.1f}% | {amount:>11.2f}")
    print("---------------------------------------------------------------------------")
    print(f"{'TOTAL':<50} | {'100.0%':>12} | {total_allocated_amount_check:>11.2f}")


    print("\nWhy these allocations?")
    for point in justification:
        print(f"- {point}")
    print("\n- Learn more about general investing principles at: https://www.investopedia.com/financial-advisor/asset-allocation/\n"
          "  and for Indian context: https://www.amfiindia.com (Investor Education section)")


def provide_general_financial_tips(lifecycle_stage, monthly_savings, original_salary):
    """Provides general financial tips."""
    print("\n--- General Financial Tips ---")
    tips = []
    if monthly_savings <= 0 and original_salary > 0 :
        tips.append("- Your expenses currently meet or exceed your income. Focus on creating a budget to identify areas to cut back or explore ways to increase your income.")
    elif original_salary > 0:
        savings_rate = (monthly_savings / original_salary) * 100
        tips.append(f"- Your current savings rate is approximately {savings_rate:.1f}%. Aim for at least 20-30% if possible, especially in accumulation stages.")
        if savings_rate < 15:
             tips.append("- Consider reviewing discretionary spending to boost your savings rate.")

    tips.append("- Build & Maintain an Emergency Fund: Aim for 3-6 months of essential living expenses in a safe, liquid account (e.g., savings account, liquid mutual fund). This is your top priority before aggressive investing.")
    tips.append("- Get Adequately Insured: Ensure you have sufficient health insurance for yourself and your family. If you have dependents, a term life insurance policy is crucial.")
    tips.append("- Invest Regularly & Be Disciplined: Consistency through SIPs (Systematic Investment Plans) is key, especially for long-term goals. Don't try to time the market.")
    tips.append("- Review Periodically: Revisit your financial plan and investments at least annually, or when major life events occur (marriage, new job, child, etc.).")
    tips.append("- Understand Your Investments: Before investing in any product, understand its risks, costs (like expense ratios for MFs), and how it fits your goals.")

    if lifecycle_stage == "Student" or lifecycle_stage == "Early Career":
        tips.append("- Focus on upskilling and career growth to increase your earning potential. Your human capital is your biggest asset at this stage.")
    if lifecycle_stage == "Late Career/Pre-Retirement" or lifecycle_stage == "Retired":
        tips.append("- Plan for healthcare expenses in retirement. Consider if your current investments align with generating regular income if needed.")

    for tip in tips:
        print(f"* {tip}")

# --- Main Program Flow (Simplified) ---
def main_simplified():
    print("╔══════════════════════════════════════════════════════════════╗")
    print("β•‘          Simplified Monthly Savings Allocator                β•‘")
    print(f"β•‘                 Today's Date: {datetime.date.today().strftime('%Y-%m-%d')}                  β•‘")
    print("β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•")
    print("\nWelcome! This tool will help you allocate your monthly savings.")
    print("This is for educational purposes and NOT financial advice.\n")

    # 1. Get User Profile
    lifecycle, risk, horizon = get_user_profile()
    print(f"\nYour Profile Summary: Lifecycle: {lifecycle}, Risk: {risk.capitalize()}, Horizon: {horizon.capitalize()}")

    # 2. Get Financial Details
    salary, expenses, savings = get_financial_details()

    if savings <= 0:
        print("\nSince you don't have a monthly surplus, investment allocation is not applicable now.")
        print("Focus on budgeting and increasing savings first.")
    else:
        # 3. Allocate Savings
        allocations_dict, justification_list = allocate_savings_simplified(savings, risk, horizon, lifecycle)

        # 4. Display Plan
        display_investment_plan(savings, allocations_dict, justification_list)

    # 5. Provide General Tips
    provide_general_financial_tips(lifecycle, savings, salary)

    # 6. Disclaimer
    print("\n\n--- Disclaimer ---")
    print("The information and suggestions provided by this script are for general guidance and educational purposes ONLY.")
    print("This does NOT constitute financial, investment, tax, or legal advice.")
    print("Investment decisions involve risks. Past performance is not indicative of future results.")
    print("Asset allocation models are generalized and may not be suitable for your individual circumstances.")
    print("It is strongly recommended to consult with a SEBI-registered Investment Adviser or a qualified financial professional for personalized advice.")

    print("\nBudget calculation and investment suggestions complete. Plan wisely!\n")

if __name__ == "__main__":
    main_simplified()