OpenRules Implementation of the Loan Approval Challenge

loanapprovalimageIn Dec-2016 DMCommunity.org published a new Challenge called “Loan Approval“. This challenge actually was proposed by myself based on real-world experience with one of our customers (a large bank). The most important point of this challenge was a general architecture that allows to invoke the same business logic based on real-time events coming from external sources. I described how such architecture may look like at this video that demonstrates a decision engine integrated with a PUB/SUB message broker.

However, the business logic itself also may be interesting especially if implemented using the latest DMN approach. So, I decided to do a new implementation that is described below. Everything is defined in Excel tables with zero Java code.

Here is the problem scenario:

loanapprovalscenario

On the top-level our decision “DetermineLoanStatus” consists of several sub-decisions:

loanapproval1

I will assume that we have an array of securities which we should process (iterate through) to calculate Total Equity and Total Debt of All Securities. I decided to describe all currebtly known securities in the Data array “securities” as follows:

loanapproval2

The proper Datatype is also defined directly in Excel:

loanapproval3

To be able to iterate through a list of securities using OpenRules default iteration constructs (without explicit loops) I defined a new type “SecurityList”:

loanapproval4

and created the proper list as follows:

loanapproval5

To finish with data I will also show how I presented our Borrower:

loanapproval6

And finally here is the Loan Application:

loanapproval7

loanapproval8

Now we may define the business logic that implements 3 sub-decisions mentioned above. The following decision table

loanapproval9

will initialize two decision variables “Total Debt for All Securities” and “Total Equity for All Securities” with zeros and iterate through all Securities by executing the rules “ProcessOneSecurity” for all of them. Actually, “ProcessOneSecurity” is a decision table that simply increments the mentioned variables with the proper values for the current security:

loanapproval10

This is the way how OpenRules avoids using explicit programming loops (in particular, available in DMN FEEL).

The following decision tables calculates all totals needed to calculate Loan Status:

loanapproval11

And finally we may calculate Loan Status using the following decision table:

loanapproval12

To make our decision model executable, we only need to add a glossary that puts together all decision variables used in the above decision tables. Here it is:

loanapproval13

Now we are ready to execute the decision model. Here are the execution results:

*** Decision DetermineLoanStatus ***
Decision has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
Conclusion: Total Equity for All Securities += Security Equity [300000.0]
Conclusion: Total Debt for All Securities += Security Debt [150000.0]
Conclusion: Total Equity for All Securities += Security Equity [326000.0]
Conclusion: Total Debt for All Securities += Security Debt [350000.0]
Conclusion: Total Equity for All Securities += Security Equity [566000.0]
Conclusion: Total Debt for All Securities += Security Debt [500000.0]
Conclusion: Total Equity for All Securities += Security Equity [566000.0]
Conclusion: Total Debt for All Securities += Security Debt [550000.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [681200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [690000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = DECLINED [DECLINED]
Decision has been finalized

Then I decided to improve my output by creating different lists of securities and showing the results as (Global Equity – Global Debt). Here are the proper changes in security lists:

loanapproval14Here I’ve added an empty security “None” to cover the very first case  with no securities. Then I defined 5 different security lists:

loanapproval15

I also added this simple decision table to display the results:

loanapproval16

I also added test-cases for all security lists:

loanapproval17

After successfully running all these test-cases, I decided to display the name of each security while we are processing them to better understand the processing logic. So, I added one more column to the decision table “ProcessOneSecurity”:

loanapproval18

And the final results look exactly as in the requested problem description:

*** Decision DetermineLoanStatus ***
Decision has been initialized

RUN TEST: Test 1
Decision Run has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
None [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [0.0]
Conclusion: Total Debt for All Securities += Security Debt [0.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [115200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [140000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = DECLINED [DECLINED]
Decision DetermineLoanStatus: Display (Global Equity – Global Debt)
Global Equity – Debt = -24800.0 [produced by DisplayResults]
Decision has been finalized

RUN TEST: Test 2
Decision Run has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
Joe & Dawn Johnson [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [300000.0]
Conclusion: Total Debt for All Securities += Security Debt [150000.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [415200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [290000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = APPROVED [APPROVED]
Decision DetermineLoanStatus: Display (Global Equity – Global Debt)
Global Equity – Debt = 125200.0 [produced by DisplayResults]
Decision has been finalized

RUN TEST: Test 3
Decision Run has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
Joe & Dawn Johnson [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [300000.0]
Conclusion: Total Debt for All Securities += Security Debt [150000.0]
Joe Johnson & Bill Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [326000.0]
Conclusion: Total Debt for All Securities += Security Debt [350000.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [441200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [490000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = DECLINED [DECLINED]
Decision DetermineLoanStatus: Display (Global Equity – Global Debt)
Global Equity – Debt = -48800.0 [produced by DisplayResults]
Decision has been finalized

RUN TEST: Test 4
Decision Run has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
Joe & Dawn Johnson [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [300000.0]
Conclusion: Total Debt for All Securities += Security Debt [150000.0]
Joe Johnson & Bill Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [326000.0]
Conclusion: Total Debt for All Securities += Security Debt [350000.0]
Bill & Susan Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [566000.0]
Conclusion: Total Debt for All Securities += Security Debt [500000.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [681200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [640000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = APPROVED [APPROVED]
Decision DetermineLoanStatus: Display (Global Equity – Global Debt)
Global Equity – Debt = 41200.0 [produced by DisplayResults]
Decision has been finalized

RUN TEST: Test 5
Decision Run has been initialized
Decision DetermineLoanStatus: Process All Securities
Assign: Total Debt for All Securities = 0 [0.0]
Assign: Total Equity for All Securities = 0 [0.0]
Execute Rules for a collection Securities of the type Security
Joe & Dawn Johnson [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [300000.0]
Conclusion: Total Debt for All Securities += Security Debt [150000.0]
Joe Johnson & Bill Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [326000.0]
Conclusion: Total Debt for All Securities += Security Debt [350000.0]
Bill & Susan Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [566000.0]
Conclusion: Total Debt for All Securities += Security Debt [500000.0]
Tommy Smith [produced by ProcessOneSecurity]
Conclusion: Total Equity for All Securities += Security Equity [566000.0]
Conclusion: Total Debt for All Securities += Security Debt [550000.0]
Decision DetermineLoanStatus: Calculate Totals
Assign: Borrower Total Equity = Borrower Monthly Income * 0.8 * Loan Term [115200.0]
Assign: Borrower Total Debt = Borrower Monthly Debt * Loan Term [90000.0]
Assign: Global Equity = Total Equity for All Securities + Borrower Total Equity [681200.0]
Assign: Global Debt = Total Debt for All Securities + Borrower Total Debt + Loan Amount [690000.0]
Decision DetermineLoanStatus: Calculate Loan Status
Assign: Loan Status = DECLINED [DECLINED]
Decision DetermineLoanStatus: Display (Global Equity – Global Debt)
Global Equity – Debt = -8800.0 [produced by DisplayResults]
Decision has been finalized

As you can see, the results exactly correspond to those described in the problem scenario.

You will be able to download and try this decision model with the next OpenRules release – it will be included into the standard workspace “openrules.dmn” under the name “DecisionLoanApproval”. Meanwhile, if you want to try this decision model yourself, just send me a request to jacobfeldman@openrules.com.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.