Technical Notes: HomeCredit Scout (FastAPI + React + LightGBM)
Technical notes on HomeCredit Scout: a FastAPI + React + LightGBM loan pre-check demo built beyond a Power BI coursework dashboard—covering feature design, risk thresholds, max-amount search, i18n, and privacy consent.
Technical Notes: HomeCredit Scout (FastAPI + React + LightGBM)
A compact engineering write-up of how I turned the Home Credit Default Risk dataset into a runnable loan pre-check demo—covering model design, API decision logic, multilingual UI, and privacy consent.
Repo: github.com/petercontinue/HomeCreditDefaultRisk
Related post: From Power BI to a full-stack loan pre-check demo
---
1. Motivation
The coursework requirement was a Power BI dashboard. After finishing that, I wanted a second artifact on the same domain:
- take form inputs a real user could provide
- score default risk with a trained model
- return approve / decline, and if approved, a max loan amount
- persist submissions and show readable feedback
That became HomeCredit Scout.
---
2. High-level architecture
React (Vite) ──JSON──► FastAPI
│
├─ feature build + LightGBM predict
├─ threshold decision + amount search
├─ localized feedback text
└─ PostgreSQL (applications + consent audit)Runtime split:
| Component | Role |
|---|---|
ml/ | Offline training → artifacts under ml/artifacts/ |
backend/ | FastAPI inference, rules, i18n messages, DB |
frontend/ | Multi-step form, result page, language switcher, privacy page |
| Docker | PostgreSQL only (host port 6436) |
Frontend and backend run on the host. This kept iteration fast while tuning features and UX.
---
3. Machine learning choices
3.1 Problem framing
Kaggle’s label is TARGET (payment difficulty / default-related), not “approved amount”. So the product layer is:
- estimate (P(\text{default}))
- approve if (P < \text{threshold})
- if approved, search the largest credit amount that still satisfies the threshold (with income / hard caps)
3.2 Feature strategy
application_train.csv has 100+ columns. A public form cannot collect bureau history or external scores cleanly, so the demo uses ~25 user-facing fields plus engineered ratios, for example:
- age / employment years (from
DAYS_*style fields) - credit-to-income, annuity-to-income, credit-to-goods
Categorical fields keep training vocabulary (e.g. Married, Working) so inference matches the model. UI chrome is translated; many dropdown values stay English for model compatibility.
3.3 Model
- Algorithm: LightGBM binary classifier
- Preprocessing: median imputation for numerics + category codes (saved with
joblib) - Artifacts:
model.txtpreprocessor.joblibfeature_meta.json(threshold, risk bands, version)metrics.json
Validation AUC on this reduced feature set is roughly 0.69—fine for a demo, not a competition-winning pipeline.
Large raw CSVs are gitignored; pretrained artifacts are committed so the API can run without re-downloading the full dataset.
---
4. Decision engine (backend)
Core flow in the API:
- Validate payload (Pydantic)
- Build the same feature vector as training
- Predict default probability
- Compare against
approval_threshold(from artifacts, overridable via env) - On approve: binary-search max
AMT_CREDITwhile scaling annuity/goods proportionally - Build feedback bullets from simple business rules (income burden, employment tenure, assets, etc.)
- Persist row to PostgreSQL
This separation matters: the model scores risk; rules turn risk into a product response.
---
5. Frontend notes
- React + TypeScript + Vite
- Three-step form: personal → income/work → loan intent
- Result page: verdict, probability, max amount, feedback lists
- Responsive layout for phone / tablet / desktop
- Language preference stored in
localStorage
Predict requests send:
langin JSON bodyX-Lang/Accept-Languageheaders
so backend feedback matches the active UI language.
---
6. Internationalization
Supported locales:
enzh-CNzh-TWjako
Implementation pattern:
- Frontend copy:
frontend/src/i18n/locales/* - Backend feedback / validation strings:
backend/app/i18n/messages.py
Money and datetime formatting follow the active locale (English uses month/day/year style).
---
7. Privacy & consent (demo-level)
Because submissions are stored and the app is public/no-login, I added:
/privacyPrivacy Notice (localized)- mandatory consent checkbox before submit
- server-side rejection if consent is missing or notice version mismatches
- DB fields:
consent_accepted,privacy_notice_version,consent_accepted_at
The notice is written with New Zealand Privacy Act 2020 information privacy principles in mind for a demo context. It is not legal advice.
Current notice version constant: 1.0.0 (keep frontend and backend in sync when you edit the notice).
---
8. Local run (short)
From the project root on Windows:
# DB
start-db.cmd
# Backend
start-backend.cmd
# Frontend
start-frontend.cmdThen open http://127.0.0.1:5173.
Useful endpoints:
GET /api/healthGET /api/meta/form-optionsPOST /api/predict- Swagger:
http://127.0.0.1:8000/docs
Retrain (needs local dataset CSVs):
python ml/train.pyRestart the backend after retrain so it reloads artifacts.
---
9. What I would improve next
- Probability calibration (Platt / isotonic) for more interpretable thresholds
- Stronger feature importance explanations (e.g. SHAP) mapped to user-facing text
- Optional “what-if” slider for requested amount without full resubmit
- Proper migration tool instead of lightweight
ALTER TABLEhelpers - Deployed demo environment with retention / delete-by-application-id workflow
---
10. Closing
Power BI was the right tool for the assignment’s storytelling. The full-stack demo was the right tool for learning how analytics work turns into APIs, validation, UX, and operational details.
If you clone the repo, start with the README and the pretrained artifacts; only pull the Kaggle CSVs if you want to retrain.
GitHub: https://github.com/petercontinue/HomeCreditDefaultRisk
---
Disclaimer: HomeCredit Scout is for learning and demonstration only. It does not provide credit advice and is not a real lending decision system.
About petercontinue
peter love study
Comments
Sign in to leave a comment.