Scoring is the core decision-making module in a lending process. It identifies the creditworthiness of a customer based on credit files & other related information. Credit file information is usually provided by an independent credit bureau.
The algorithm for finding a score varies with the type of lending & the lending institution. While developing lending software for a leading mortgage institution, we faced a situation wherein the final score was based on multiple sets of information known as ‘scoring variables’. The ‘variables’ set included basic customer parameters like ‘Age’, ‘Income’ & information received from independent Credit bureaus. These variables were of different types; such as a numeric field like ‘Age’ or pre-defined text like ‘Pay-Frequency’.
The first decision we had to make was to either go with a third party business engine that handles scoring or to create one of our own. There are vast variety of business engines available in the market. But our focus was on maintainability, ability to handle complex business rules & cost. We decided to create our own scoring engine.
For building a scoring engine, we first looked at the Windows Workflow Foundation (WF). WF is flexible enough for handling complex sets of algorithms and it gives a nice user interface for the developer implementing it. But with the response time, we could manage with WF and the data-driven approach which we were looking for, WF was not a practical option for the requirements of our scoring algorithm.
‘Expressions Trees’ in .Net represent codes in a tree-data structure. Each node is some kind of expressions like a lambda expression or a method call. Please note that Expression Trees widely use DLR (Dynamic Language Runtime). It fits nicely into the scoring engine because the codes written as expressions can be compiled & run dynamically. This enables dynamic changes to the algorithm & its variable usage. This would mean that the participating code can be fed into the scoring engine from outside & can be stored in Databases, xml or other data structures. Since scoring engines involve a lot of binary expressions (like Age > 60 => Do something) Expression Trees are very helpful. Expression Trees are created using the System.Linq.Expressions namespace.
A simple illustration of constant integer value comparison:
static void Main(string[] args)
{
SpaceSplitParseNumericExpression(“8 GreaterThan 4”);//Returns true (8 > 4)
SpaceSplitParseNumericExpression(“5 LessThan 1”); //Returns false (5 < 1)
Console.WriteLine(“Press any key to exit”);
Console.Read();
}
public static void SpaceSplitParseNumericExpression(string expression)
{
string[] tokens = expression.Split(‘ ‘);
bool output = Expression.Lambda<Func<bool>>(
Expression.MakeBinary(
(ExpressionType)Enum.Parse(typeof(ExpressionType), tokens[1]),
Expression.Constant(Convert.ToInt32(tokens[0])),
Expression.Constant(Convert.ToInt32(tokens[2])))
).Compile()();
Console.WriteLine(string.Format(“Expression ‘{0}’, returns: {1}”,
expression, output));
}
For an actual scoring engine, Parameter Expressions are used along with Constant Expressions, to make this logic more dynamic & flexible. These parameters & constants can then be stored into a DB or any other data source which can be read by the engine on a need-basis. Thus we ended up creating a scoring engine which does not have hardcoded numbers in the code and is compact, fast & easily maintainable.
< 1)
Console.WriteLine(“Press any key to exit”);
Console.Read();
}
public static void SpaceSplitParseNumericExpression(string expression)
{
string[] tokens = expression.Split(‘ ‘);
bool output = Expression.Lambda(
Expression.MakeBinary(
(ExpressionType)Enum.Parse(typeof(ExpressionType), tokens[1]),
Expression.Constant(Convert.ToInt32(tokens[0])),
Expression.Constant(Convert.ToInt32(tokens[2])))
).Compile()();
Console.WriteLine(string.Format(“Expression ‘{0}’, returns: {1}”,
expression, output));
}
For an actual scoring engine, Parameter Expressions are used along with Constant Expressions, to make this logic more dynamic & flexible. These parameters & constants can then be stored into a DB or any other data source which can be read by the engine on a need-basis. Thus we ended up creating a scoring engine which does not have hardcoded numbers in the code and is compact, fast & easily maintainable.