Inspiration

I'm an industrial engineer, so I'm always looking for ways to optimize processes and make life easier for the people who run them. If a process can be standardized, I think it should be. Deciding whether to give credit to a company is one of those processes. Today it's settled with Excel, a bureau score and the salesperson's gut feeling, and two people can give different answers for the same customer. I wanted to turn it into a standard process with the same rules for everyone, where the receivables portfolio, each customer's risk and the credit decision live in one place, and every recommendation comes with its reasons.

What it does

ISA Risk takes a credit request and answers four questions: approve, approve with conditions or reject, how much credit, on what terms and who has to sign. It also gives the probability of a delay at 30, 60 or 90 days.

There are three profiles, and each one sees only what it needs:

  • Management: executive dashboard, decisions inbox and the Customer 360 view.
  • Analyst: their own queue of requests to evaluate.
  • Sales: "would my customers be approved today?" before offering credit, plus a form to register a new request. While they fill it in, the model shows its decision, the possible credit line and the approver. The request then reaches the inbox, and Management approves or rejects it.

In the Customer 360 view, a 30/60/90 selector changes the probability, the decision, the credit line, the terms and the approver live.

How we built it

The model is deliberately simple and explainable. The probability of delay at horizon $h \in {30, 60, 90}$ days comes from a logistic regression with fixed weights over 7 customer data points:

$$P(\text{delay}h) = \frac{1}{1 + e^{-z_h}}, \qquad z_h = b_h + \sum{i=1}^{7} w_i x_i$$

The inputs are average delay, maximum delay, credit line usage, customer tenure, financial system rating, bank debt over sales and protested documents. Each horizon has its own intercept $b_h$, and the approval limit gets stricter as the horizon gets longer: approve up to 5 % at 30 days, 2.5 % at 60 and 1.2 % at 90.

On top of that, a consolidated score from 0 to 100 (higher means more risk) adds up 7 signals, each with a cap: probability of delay (25), days of delay (20), overdue balance over debt (15), line usage (15), bureau score (15), sector risk (5) and overdue trend (5). It defines four levels: Low, Medium, High and Critical.

A few hard rules override the model. A Doubtful or Loss rating means rejection. Protested documents, delays over 90 days or more than 30 % of the debt overdue block approval without conditions.

The suggested credit line is the smaller of what was requested and what the customer can handle:

$$\text{line} = \min!\Big(\text{requested},\; \min\big(\text{monthly purchases} \cdot \tfrac{\text{term}}{30} \cdot 1.5 \cdot f_{\text{level}},\; 0.10 \cdot \text{annual sales}\big)\Big)$$

where $f_{\text{level}}$ is 1.0, 0.75, 0.45 or 0.2 depending on the risk level. The approver depends on whichever is higher, the amount or the term: Credit Analyst, Head of Credit and Collections, Finance Management or Credit Committee.

The app is plain HTML, CSS and vanilla JavaScript, so it opens with a double-click on index.html, with no install, server or internet. Demo requests are saved in the browser. The portfolio is synthetic: a Python script generates 60 customers across six sectors from a hidden "latent risk", so the data stays coherent (a customer who pays late also tends to use more of their line and have a worse rating). It uses a fixed seed, so it always produces the same portfolio. I planned the scope, requirements and technical spec with the Devpost Learn Skill Pack and built the app with AI assistance.

Challenges we ran into

  • My first version was too simple. It had a decision box and little else. I rebuilt it with a full dashboard, an inbox, a complete Customer 360 view and the consolidated score.
  • No real data. I could not use real customers, so I had to make fake ones that behave like real ones. Random numbers were not enough, which is why I generated everything from a latent risk.
  • Explainability. A trained model would have been harder to defend in front of a credit committee. Fixed weights and written rules let every decision be traced back to its reasons.
  • Making it feel like a real workflow. The request needed to travel from Sales to the inbox to Management, which meant handling profiles, states and persistence without a backend. ## Accomplishments that we're proud of I turned a process that depends on judgment into one with the same rules for everyone, and every recommendation shows its reasons. A salesperson can register a request, see what the model would say while filling it in, and follow it all the way to Management's decision, in a single app that opens with a double-click. ## What we learned
  • A transparent model with the reasons in plain sight is easier to trust than a more accurate one nobody can explain.
  • Thresholds that change with the horizon say something real: the longer the delay you care about, the less risk you accept.
  • Planning the scope before building saved me from adding screens that did not help the decision. ## What's next for ISA RISK Connect it to real customer and bureau data, add reading of bureau reports in PDF, train and compare a model against the fixed-weights one, and link it to an ERP so requests do not need to be typed in.

All customers, names, amounts and scores are fictional.

Built With

Share this project:

Updates