Spaces:
Sleeping
Sleeping
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() |