A borrower's utilisation moves from 35% to 92% in one day while payment behaviour deteriorates. The behavioural score refreshes overnight and sees the change at 08:00—after more exposure may have been drawn and the intervention window narrowed.
PAYMENT DETERIORATESnightly batchNEXT DAY 08:00SIGNAL APPEARS
Update affected feature state instead of replaying all history
type BehaviouralFeatureState = {
partyId: string;
utilisationCurrent?: number;
utilisation30dAvg?: number;
paymentCount30d: number;
latePaymentCount90d: number;
maxDpd90d: number;
effectiveAsOf: Date;
updatedAt: Date;
featureSetVersion: string;
};| Type | Examples | Required state |
|---|---|---|
| Base | Current utilisation, current DPD, recent payment amount | Latest canonical facility/account state |
| Derived | 30-day average utilisation, payment trend, DPD velocity | Window history, baseline and expiry semantics |
Drawdown, repayment, limit change and reversal update canonical facility state first. Utilisation then uses a point-in-time-consistent numerator and denominator; consuming raw vendor events independently can combine fresh drawn with a stale limit.
A rolling window must add new evidence and expire old evidence
expire beyond T−30d→ACTIVE WINDOW
(T−30d, T]←NEW EVENTS
enter by event time
type TimedValue = { time: Date; value: number };
type RollingWindowState = { events: TimedValue[]; sum: number };
function updateRollingSum(
state: RollingWindowState,
event: TimedValue,
cutoff: Date
): RollingWindowState {
const events = [...state.events, event]
.filter((x) => x.time > cutoff);
return { events, sum: events.reduce((s, x) => s + x.value, 0) };
}The example favours clarity over performance. High-volume paths use deques, ordered state or time buckets. Hourly counts and daily averages reduce state, but bucket granularity must preserve the decision semantics; a daily bucket can erase an intraday deterioration path.
Event-time windows must converge under late and out-of-order arrival
A payment effective Monday but received Wednesday belongs economically to Monday if the feature contract uses effective time. Processing it as Wednesday distorts the trend.
The live current feature may correct when the late payment arrives; the historical decision snapshot remains immutable. Insert the late event into window state, recompute affected aggregates and preserve revision lineage.
| Feature class | Examples | State requirement |
|---|---|---|
| Commutative fixed-set | Sum, count, maximum | Converges for same eligible event set |
| Sequence-sensitive | Missed-payment streak, time since last payment | Ordered events and explicit tie semantics |
| Non-monotonic rolling | Max DPD in last 90 days | Retain candidates when current maximum expires |
Level, velocity and deterioration describe different borrower paths
A move from 20% to 60% can convey different information from a stable 65%; the path matters alongside level. Velocity can apply to exposure, DPD, payment ratio or behavioural PD. Acceleration is possible, but added sophistication must prove operational value.
Payment ratio itself needs versioned applied-payment, scheduled-amount, allocation and reversal semantics. These are engineering inputs, not merely arithmetic.
Persistence, hysteresis, debounce and cooldown solve different noise problems
type SignalState = {
consecutiveBreaches: number;
firstBreachAt?: Date;
lastBreachAt?: Date;
};| Control | When it acts | Purpose |
|---|---|---|
| Persistence | After repeated/durable breach | Reject transient deterioration |
| Hysteresis | Across trigger and clear state | Prevent boundary oscillation |
| Debounce | Before scoring | Coalesce rapid events while state settles |
| Cooldown | After decision/action | Suppress repeated workflow without material new state |
Feature updates do not imply continuous model rescoring
Rescore on material DPD transition, utilisation change, payment failure or a governed schedule. A streaming feature can also fire a direct rule without model execution. Hybrid event and periodic triggers are often safer.
type BehaviouralScoreState = {
partyId: string;
score: number;
pd?: number;
scoredAt: Date;
featureSnapshotId: string;
modelVersion: string;
triggerType: string;
};Record triggering event/state, feature versions, model version, score and rule path. Require a governed material score change such as |ΔPD| > ε plus persistence before operational routing; no universal ε applies.
Rules above the model encode data quality, persistence, cooldown and operational materiality. They must remain separate from the model’s risk estimate.
A missing source is not negative borrower behaviour
type FeatureHealth = {
status: "FRESH" | "STALE" | "MISSING" | "ERROR";
asOf: Date;
};True silence—an expected salary or payment did not arrive—is different from a failed source feed. Do not trigger missed-payment risk when payment data are unavailable. A low-confidence risk signal can route differently from a high-confidence one according to policy.
Mixed-cadence vectors must expose exact freshness. If a critical feature breaches its age budget, use an approved fallback, defer or refer; never silently replace missing with zero.
Periodic full replay controls persistent incremental error
Incremental corruption persists until detected. Recompute sampled populations from canonical history and compare live state to catch missed expiry, duplicate events and reducer bugs. Keep a periodic full rescore as safety for missed events, stale state and non-event features.
material changes+SCHEDULED RECOMPUTE
completeness control→CONSISTENT EWS STATE
Measure event-to-intervention latency, not feature speed alone
A 20-hour feature-latency improvement creates little value if operations still review once daily. Signals have different half-lives: payment failure may justify faster reaction than a long-term utilisation trend.
Early-warning quality combines predictive value with operational usability. Monitor subsequent deterioration, action utility and false-positive burden—not sensitivity alone.
A two-day deterioration should become WATCH before it becomes noise
| Checkpoint | Utilisation | Payment state | Signal state |
|---|---|---|---|
| Start | 38% | Ratio 1.0; DPD 0 | NORMAL |
| First drawdown | 72% | Unchanged | WATCH candidate; persistence starts |
| Second drawdown | 91% | Unchanged | WATCH confirmed by velocity |
| Scheduled payment missed | 91% | Ratio deteriorates; DPD moves | ALERT / controlled review |
| Partial payment | Lower drawn | Partial cure evidence | Remain governed by persistence/hysteresis |
| Behavioural rescore | Updated vector | Worse score | Route to review, not automatic adverse action |
A nightly path sees the combined change next morning. Streaming preserves more lead time. By contrast, one isolated utilisation spike followed by immediate repayment does not survive persistence and hysteresis, so it should not create an alert.
Event-driven does not mean event-only
Window contributions expire even when no new transaction arrives. TimeSinceLastPayment changes continuously. Use timers, on-read computation or periodic refresh according to latency needs.
Absence can be a signal only when an expected-event definition exists and source health is good. A timer must never confuse pipeline silence with customer silence.
A golden behavioural stream proves state at every checkpoint
| Test | Expected proof |
|---|---|
| Golden stream | Utilisation, payment, missed due, partial payment, reversal and late payment produce fixed features and trigger states |
| Incremental equality | Incremental features equal full replay at every checkpoint |
| Out-of-order | Late insertion converges to ordered-stream final state |
| Window expiry | Old contributions expire when time advances without a transaction |
| Hysteresis | 79 → 81 → 79 → 82 → 78 does not churn alerts |
| Debounce/cooldown | Rapid events create one score; repeated unchanged risk creates no action spam |
| Known/restated | Late correction adjusts current state but not stored historical decision |
| Source outage | Missing feed blocks absence-based alert |
| Periodic control | Scheduled full recompute detects injected incremental corruption |
Monitor feature integrity, signal stability and intervention value
Track customers across NORMAL, WATCH, ALERT and COOLDOWN. Analyse trigger concentration by feature, event type, product and source. A sudden alert-rate increase can reflect genuine deterioration, a source change, a state-engine bug or late-event surge.
The Entimema architecture updates quickly and acts deliberately
- Identify material behavioural state
- Define canonical event inputs
- Define rolling window
- Define incremental update
- Handle time expiry
- Handle late events
- Stabilise signal
- Define rescore trigger
- Measure lead time
- Monitor outcomes
A Behavioural Signal Integrity Agent can diagnose alerts without acting on customers
A controlled agent can monitor feature freshness, compare incremental with full replay, detect rolling-window inconsistencies and late corrections, inspect abnormal utilisation or payment changes, detect source-health false signals, measure alert churn and quantify intervention lead time.
Credit Risk
Govern behavioural signals, validation and alert performance.
Decision Automation
Stabilise model and rule triggers before controlled workflows.
Financial Data
Preserve event time, rolling state, corrections and lineage.
Continue with From Batch ETL to Event-Driven Credit Risk Architecture, Point-in-Time Correct Features, Building a Credit Risk Feature Store, Building a Reliable DPD Engine, Reconstructing Account State, Early Warning Systems and Behavioural Credit Scoring. Real-time exposure, event triggers and production EWS monitoring remain future research directions—not fabricated routes.