A lightweight library for real-time, consistent and scalable coordination of events.
durable_rules is a polyglot micro-framework for real-time, consistent and scalable coordination of events. With durable_rules you can track and analyze information about things that happen (events) by combining data from multiple sources to infer more complicated circumstances.
A full forward chaining implementation (A.K.A. Rete) is used to evaluate facts and massive streams of events in real time. A simple, yet powerful meta-liguistic abstraction lets you define simple and complex rulesets, such as flowcharts, statecharts, nested statecharts, paralel and time driven flows.
Let’s consider a couple of fictitious fraud rules used in bank account management.
Note: I'm paraphrasing the example presented in this article.
- If there are two debit requests greater than 200% the average monthly withdrawal amount in a span of 2 minutes, flag the account as medium risk.
- If there are three consecutive increasing debit requests, withdrawing more than 70% the average monthly balance in a span of three minutes, flag the account as high risk.
Durable.ruleset :fraud_detection do# compute monthly averageswhen_all span(86400), (m.t == "debit_cleared") | (m.t == "credit_cleared") dodebit_total = 0credit_total = 0for tx in m doif tx.t == "debit_cleared"debit_total += tx.amountelsecredit_total += tx.amountendends.balance = s.balance - debit_total + credit_totals.avg_balance = (s.avg_balance * 29 + s.balance) / 30s.avg_withdraw = (s.avg_withdraw * 29 + debit_total) / 30end# medium risk rulewhen_all c.first = (m.t == "debit_request") &(m.amount > s.avg_withdraw * 2),c.second = (m.t == "debit_request") &(m.amount > s.avg_withdraw * 2) &(m.stamp > first.stamp) &(m.stamp < first.stamp + 120) doputs "Medium risk"end# high risk rulewhen_all c.first = m.t == "debit_request",c.second = (m.t == "debit_request") &(m.amount > first.amount) &(m.stamp < first.stamp + 180),c.third = (m.t == "debit_request") &(m.amount > second.amount) &(m.stamp < first.stamp + 180),s.avg_balance < (first.amount + second.amount + third.amount) / 0.7 doputs "High risk"endendDurable.run_all
from durable.lang import *import timewith :# compute monthly averagesdebit_total = 0credit_total = 0for tx in c.m:if tx.t == 'debit_cleared':debit_total += tx.amountelse:credit_total += tx.amountc.s.balance = c.s.balance - debit_total + credit_totalc.s.avg_balance = (c.s.avg_balance * 29 + c.s.balance) / 30c.s.avg_withdraw = (c.s.avg_withdraw * 29 + debit_total) / 30# medium risk ruleprint('Medium Risk')# high risk ruleprint('High Risk')
var d = require'durable';with druleset'fraudDetection'// compute monthly averageswhenAllspan86400 ormteq'debitCleared' mteq'creditCleared'var debitTotal = 0;var creditTotal = 0;for var i = 0; i < cmlength; ++iif cmit === 'debitCleared'debitTotal += cmiamount;elsecreditTotal += cmiamount;csbalance = csbalance - debitTotal + creditTotal;csavgBalance = csavgBalance * 29 + csbalance / 30;csavgWithdraw = csavgWithdraw * 29 + debitTotal / 30;;// medium risk rulewhenAllcfirst = andmteq'debitRequest'mamountgtcsavgWithdrawmul2csecond = andmteq'debitRequest'mamountgtcsavgWithdrawmul2mstampgtcfirststampmstampltcfirststampadd120console.log'Medium risk';;// high risk rulewhenAllcfirst = mteq'debitRequest'csecond = andmteq'debitRequest'mamountgtcfirstamountmstampltcfirststampadd180cthird = andmteq'debitRequest'mamountgtcsecondamountmstampltcfirststampadd180savgBalanceltaddcfirstamount csecondamount cthirdamountdiv0.7console.log'High risk';;drunAll;