Spaces:
Build error
Build error
| """ | |
| AI Loan Eligibility Predictor for BankBot | |
| Predicts loan approval chance and EMI affordability using ML | |
| """ | |
| import numpy as np | |
| import pandas as pd | |
| from sklearn.preprocessing import StandardScaler | |
| from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier | |
| import pickle | |
| import os | |
| from datetime import datetime | |
| import json | |
| LOAN_MODEL_FILE = "loan_prediction_model.pkl" | |
| class LoanEligibilityPredictor: | |
| """ML-based loan eligibility prediction""" | |
| def __init__(self): | |
| self.classifier = None | |
| self.scaler = StandardScaler() | |
| self.feature_names = [ | |
| 'salary', 'credit_score', 'existing_loans', | |
| 'employment_years', 'age', 'loan_amount' | |
| ] | |
| self.load_model() | |
| def load_model(self): | |
| """Load saved model or create new one""" | |
| if os.path.exists(LOAN_MODEL_FILE): | |
| try: | |
| with open(LOAN_MODEL_FILE, "rb") as f: | |
| model_data = pickle.load(f) | |
| self.classifier = model_data.get("classifier") | |
| self.scaler = model_data.get("scaler", StandardScaler()) | |
| except Exception as e: | |
| print(f"Error loading loan model: {e}") | |
| self._initialize_model() | |
| else: | |
| self._initialize_model() | |
| def _initialize_model(self): | |
| """Initialize Random Forest for loan prediction""" | |
| # Create synthetic training data | |
| X_train = np.array([ | |
| [100000, 750, 0, 5, 35, 500000], # Approved | |
| [150000, 800, 1, 10, 42, 1000000], # Approved | |
| [200000, 780, 2, 8, 45, 1500000], # Approved | |
| [50000, 600, 3, 2, 28, 300000], # Rejected | |
| [80000, 650, 2, 3, 32, 400000], # Rejected | |
| [120000, 700, 1, 6, 38, 600000], # Approved | |
| [45000, 580, 4, 1, 25, 250000], # Rejected | |
| [180000, 770, 0, 12, 50, 900000], # Approved | |
| [70000, 620, 3, 2, 30, 350000], # Rejected | |
| [160000, 790, 1, 9, 44, 800000], # Approved | |
| ]) | |
| y_train = np.array([1, 1, 1, 0, 0, 1, 0, 1, 0, 1]) # 1=Approved, 0=Rejected | |
| # Normalize features | |
| X_train_scaled = self.scaler.fit_transform(X_train) | |
| # Train classifier | |
| self.classifier = RandomForestClassifier(n_estimators=100, random_state=42) | |
| self.classifier.fit(X_train_scaled, y_train) | |
| self.save_model() | |
| def save_model(self): | |
| """Save trained model to disk""" | |
| try: | |
| with open(LOAN_MODEL_FILE, "wb") as f: | |
| pickle.dump({ | |
| "classifier": self.classifier, | |
| "scaler": self.scaler | |
| }, f) | |
| except Exception as e: | |
| print(f"Error saving loan model: {e}") | |
| def predict_eligibility(self, salary, credit_score, existing_loans, | |
| employment_years, age, loan_amount): | |
| """ | |
| Predict loan eligibility | |
| Returns: Approval probability (0-100), risk level, recommendations | |
| """ | |
| try: | |
| # Prepare features | |
| features = np.array([[ | |
| salary, credit_score, existing_loans, | |
| employment_years, age, loan_amount | |
| ]]) | |
| # Normalize | |
| features_scaled = self.scaler.transform(features) | |
| # Predict probability | |
| approval_prob = self.classifier.predict_proba(features_scaled)[0][1] * 100 | |
| # Calculate risk level | |
| if approval_prob >= 80: | |
| risk_level = "LOW RISK ✅" | |
| elif approval_prob >= 60: | |
| risk_level = "MEDIUM RISK ⚠️" | |
| elif approval_prob >= 40: | |
| risk_level = "HIGH RISK ❌" | |
| else: | |
| risk_level = "VERY HIGH RISK ❌" | |
| return approval_prob, risk_level | |
| except Exception as e: | |
| print(f"Error in prediction: {e}") | |
| return 50, "UNKNOWN RISK" | |
| def check_eligibility_rules(self, salary, credit_score, existing_loans, | |
| employment_years, age, loan_amount): | |
| """ | |
| Check basic eligibility rules | |
| Returns: Boolean and list of issues | |
| """ | |
| issues = [] | |
| # Age check | |
| if age < 21: | |
| issues.append("Age must be at least 21 years") | |
| if age > 65: | |
| issues.append("Age exceeds maximum limit (65 years)") | |
| # Employment check | |
| if employment_years < 1: | |
| issues.append("Minimum 1 year employment required") | |
| # Credit score check | |
| if credit_score < 600: | |
| issues.append("Credit score too low (minimum 600 required)") | |
| # Salary check | |
| if salary < 25000: | |
| issues.append("Salary too low for loan eligibility") | |
| # Loan amount vs salary ratio | |
| emi_amount = calculate_emi(loan_amount, 12, 10) # Assume 12% rate, 10 years | |
| if (emi_amount / salary) > 0.5: # EMI shouldn't exceed 50% of salary | |
| issues.append(f"EMI of ₹{emi_amount:.2f} exceeds 50% of salary") | |
| # Existing loans check | |
| if existing_loans > 3: | |
| issues.append("Too many existing loans") | |
| is_eligible = len(issues) == 0 | |
| return is_eligible, issues | |
| def calculate_loan_score(self, salary, credit_score, existing_loans, | |
| employment_years, age, loan_amount): | |
| """ | |
| Calculate comprehensive loan score (0-100) | |
| Considers multiple factors | |
| """ | |
| score = 0 | |
| # Credit score weight (40%) | |
| credit_component = (min(credit_score, 850) / 850) * 40 | |
| score += credit_component | |
| # Salary weight (30%) | |
| salary_component = min((salary / 500000) * 30, 30) | |
| score += salary_component | |
| # Employment years weight (15%) | |
| employment_component = min((employment_years / 30) * 15, 15) | |
| score += employment_component | |
| # Existing loans weight (10%) - negative impact | |
| loan_penalty = min(existing_loans * 2, 10) | |
| score -= loan_penalty | |
| # Age factor (5%) - younger is better | |
| age_component = min(((65 - age) / 45) * 5, 5) | |
| score += age_component | |
| # Loan affordability (penalties if high) | |
| emi = calculate_emi(loan_amount, 12, 10) | |
| if (emi / salary) > 0.5: | |
| score -= 15 | |
| elif (emi / salary) > 0.4: | |
| score -= 10 | |
| return max(0, min(score, 100)) | |
| def calculate_emi(principal, rate_per_annum=10, years=10): | |
| """ | |
| Calculate EMI (Equated Monthly Installment) | |
| Formula: EMI = P * r * (1+r)^n / ((1+r)^n - 1) | |
| """ | |
| monthly_rate = rate_per_annum / 100 / 12 | |
| months = years * 12 | |
| if monthly_rate == 0: | |
| return principal / months | |
| emi = principal * monthly_rate * ((1 + monthly_rate) ** months) / ( | |
| ((1 + monthly_rate) ** months) - 1 | |
| ) | |
| return emi | |
| def calculate_loan_eligibility(salary, credit_score, existing_loans, | |
| employment_years, age, loan_amount): | |
| """Main function to calculate loan eligibility""" | |
| predictor = LoanEligibilityPredictor() | |
| # Check basic eligibility | |
| is_eligible, issues = predictor.check_eligibility_rules( | |
| salary, credit_score, existing_loans, employment_years, age, loan_amount | |
| ) | |
| # Get ML prediction | |
| approval_prob, risk_level = predictor.predict_eligibility( | |
| salary, credit_score, existing_loans, employment_years, age, loan_amount | |
| ) | |
| # Calculate loan score | |
| loan_score = predictor.calculate_loan_score( | |
| salary, credit_score, existing_loans, employment_years, age, loan_amount | |
| ) | |
| # Calculate EMI | |
| emi = calculate_emi(loan_amount, 12, 10) | |
| # Get recommendations | |
| recommendations = get_loan_recommendations( | |
| approval_prob, salary, credit_score, existing_loans, employment_years, emi | |
| ) | |
| result = { | |
| "approval_probability": round(approval_prob, 1), | |
| "approval_status": "APPROVED ✅" if approval_prob >= 60 else "REJECTED ❌" if approval_prob < 40 else "UNDER REVIEW ⏳", | |
| "risk_level": risk_level, | |
| "loan_score": round(loan_score, 1), | |
| "is_rule_eligible": is_eligible, | |
| "issues": issues, | |
| "emi": round(emi, 2), | |
| "total_amount": round(loan_amount + (emi * 12 * 10) - loan_amount, 2), | |
| "monthly_emi": round(emi, 2), | |
| "tenure_years": 10, | |
| "rate_per_annum": 12, | |
| "recommendations": recommendations | |
| } | |
| return result | |
| def get_loan_recommendations(approval_prob, salary, credit_score, | |
| existing_loans, employment_years, emi): | |
| """Generate personalized loan recommendations""" | |
| recommendations = [] | |
| if approval_prob >= 80: | |
| recommendations.append("✅ You are likely to get approved for this loan amount") | |
| elif approval_prob < 40: | |
| recommendations.append("❌ Your approval chances are low. Consider these options:") | |
| if credit_score < 700: | |
| recommendations.append(" • Improve your credit score to 700+") | |
| if existing_loans > 2: | |
| recommendations.append(" • Pay off existing loans to improve your profile") | |
| recommendations.append(" • Apply for a smaller loan amount") | |
| recommendations.append(" • Increase your employment tenure") | |
| else: | |
| recommendations.append("⏳ Your application will be under review") | |
| # EMI affordability | |
| emi_ratio = (emi / salary) * 100 | |
| if emi_ratio > 50: | |
| recommendations.append(f"⚠️ Your EMI (₹{emi:.2f}) is {emi_ratio:.1f}% of salary. Consider reducing loan amount.") | |
| elif emi_ratio < 30: | |
| recommendations.append(f"✅ Your EMI to salary ratio ({emi_ratio:.1f}%) is very healthy") | |
| return recommendations | |
| def generate_loan_comparison(loan_amount, rates=[9, 10, 11, 12, 13], tenure_years=[5, 7, 10]): | |
| """Generate EMI comparison for different rates and tenures""" | |
| comparison_data = [] | |
| for rate in rates: | |
| for tenure in tenure_years: | |
| emi = calculate_emi(loan_amount, rate, tenure) | |
| total_amount = (emi * 12 * tenure) | |
| interest = total_amount - loan_amount | |
| comparison_data.append({ | |
| "rate": f"{rate}%", | |
| "tenure": f"{tenure} years", | |
| "emi": round(emi, 2), | |
| "total_amount": round(total_amount, 2), | |
| "interest": round(interest, 2) | |
| }) | |
| return comparison_data | |