Operational platforms in universal banks process thousands of heterogeneous tasks daily—payment renegotiations, expiring mandates, SEPA return windows, nostro break escalations, regulatory deadline filings. A naive FIFO queue treats a low-stakes internal memo and a €50M same-day payment renegotiation identically. This paper formalises a multi-dimensional priority model that scores tasks across five orthogonal dimensions, integrates with a non-preemptive M/G/c priority queue, and provably prevents starvation through a time-dependent aging mechanism.
1. Problem Statement and Scope
Consider a banking operations platform that receives a stream of work items from multiple source systems: core banking, payment gateways, CRM, regulatory reporting, treasury, and custody. Each item demands human or automated processing within a defined time constraint. The platform must decide, at each scheduling cycle, which item to assign to the next available processor.
This decision is non-trivial because tasks differ across several incommensurable dimensions simultaneously. A payment renegotiation for a corporate client may carry a large financial exposure with a hard 17:00 CET intraday deadline. A SEPA mandate expiry may carry a smaller exposure but triggers a regulatory breach if not actioned before the Bulk Settlement cut-off. A fraud dispute may have no immediate financial impact but carries a reputational dimension and a regulatory 10-business-day SLA under PSD2. No single scalar captures all these considerations.
We define the Multi-Dimensional Priority Queue (MDPQ) as a scheduling policy that assigns to each task, at each point in time, a scalar priority score derived from a formal model of its characteristics, then executes a non-preemptive priority queue over that score.
2. Task Representation
A task is a tuple 𝒯 = ⟨ id, τa, τd, 𝒙, 𝒔 ⟩ where:
— id is a unique identifier; τa ∈ ℝ≥0 is the arrival timestamp; τd ∈ ℝ≥0 ∪ {∞} is the hard deadline (∞ for tasks without one); 𝒙 ∈ ℝd is the feature vector; and 𝒔 ∈ 𝒮 is the task type (enum).
The feature vector 𝒙 encodes all task characteristics required by the scoring model. For a banking operations context, we define d = 10 base features, expanded to 5 scoring dimensions through dimension-specific transformations:
| Feature | Symbol | Domain | Description |
|---|---|---|---|
| x1 | A | ℝ≥0 | Financial exposure amount (base currency) |
| x2 | τd − τ | ℝ | Remaining time to hard deadline at evaluation time τ |
| x3 | r ∈ {1…5} | ℕ | Regulatory obligation level (5 = breach triggers supervisory action) |
| x4 | c ∈ {1…5} | ℕ | Counterparty tier (1 = systemically important, 5 = retail) |
| x5 | Rv | [0,1] | Revenue at risk as fraction of annual relationship value |
| x6 | s ∈ 𝒮 | enum | Task type (renegotiation, mandate, dispute, return, break…) |
| x7 | Δτ = τ − τa | ℝ≥0 | Age of task since arrival |
| x8 | ndep | ℕ0 | Number of downstream tasks blocked by this one |
| x9 | ε ∈ {0,1} | 𝔹 | Escalation flag (set by business rule engine) |
| x10 | μ̂s | ℝ>0 | Expected service time for task type s (hours) |
3. Priority Dimensions
We define five orthogonal priority dimensions. Each dimension produces a normalised score in [0, 1] through a dimension-specific transformation fi : ℝd → [0,1].
Dimension 1 — Financial Exposure (𝒟fin)
Raw financial exposure exhibits heavy-tailed distribution (a few very large items, many small ones). A linear scaling would allow single large items to dominate. We apply a log-normalisation with soft truncation at a configurable ceiling Amax:
Dimension 2 — Temporal Urgency (𝒟time)
This is the most analytically interesting dimension. The urgency of a task should be low when the deadline is far away and rise sharply as it approaches. We use a hyperbolic urgency function with shape parameter α that controls how steeply urgency rises near the deadline:
Dimension 3 — Regulatory Obligation (𝒟reg)
Regulatory dimensions are inherently ordinal. We map the regulatory obligation level r ∈ {1,…,5} through a convex mapping that penalises the top levels disproportionately:
Dimension 4 — Counterparty Weight (𝒟cpty)
Counterparty importance enters the model as a concave decreasing function of tier (tier 1 = highest importance):
Dimension 5 — Dependency Weight (𝒟dep)
Tasks that block other tasks propagate their delay cost multiplicatively. A task blocking ndep downstream items has a dependency score:
4. The Aggregate Scoring Function
The aggregate priority score at evaluation time τ is a weighted sum of dimension scores modulated by a time-dependent amplification operator:
Weight calibration is the most consequential design decision. The weights w = (w1,…,w5) must be elicited from business stakeholders and validated against historical outcomes. A reference configuration for a payment operations platform:
| Dimension | Symbol | Reference Weight wi | Rationale |
|---|---|---|---|
| Financial Exposure | w1 | 0.30 | Dominant driver; direct P&L impact |
| Temporal Urgency | w2 | 0.28 | Hard deadlines are non-negotiable; close second |
| Regulatory Obligation | w3 | 0.22 | Breach has asymmetric cost; overweighted relative to frequency |
| Counterparty Weight | w4 | 0.12 | Relationship management; lower weight than operational urgency |
| Dependency | w5 | 0.08 | Indirect cost; important for long chains but rare |
"The weights are not a free parameter—they are a policy statement. Choosing w3 = 0.22 is an assertion that regulatory breach is roughly 73% as important as financial exposure, and must be defensible to the risk committee."
5. Temporal Urgency Amplification
The deadline amplification operator Ω(Δt) is a global multiplier applied to the weighted sum, distinct from the per-task urgency dimension f2. It encodes the intuition that as any task approaches its deadline, the entire priority vector should be boosted—not just the temporal component:
6. Queueing Model Integration
The MDPQ scoring function assigns tasks to priority classes (§8), which then enter a non-preemptive M/G/c priority queue. We adopt the following model: task arrivals follow a Poisson process with class-specific rates λk; service times follow a general distribution with mean μ̄k−1 and second moment E[Sk2]; there are c homogeneous parallel processors.
Utilisation
Expected Waiting Time — Non-Preemptive Priority
For a non-preemptive priority queue (higher-priority tasks cannot interrupt a task already in service), the expected waiting time for class k follows the Pollaczek–Khinchine priority formula:
SLA Breach Probability
The probability that a class-k task misses its SLA deadline τSLA can be approximated, under exponential service and Poisson arrivals, from the waiting time distribution. For M/M/1 sub-queues (one-class isolation), the complementary CDF of waiting time is:
7. Anti-Starvation via Aging
A pure priority queue where high-priority tasks continuously arrive can starve low-priority tasks indefinitely. This is unacceptable in a banking platform—every task has an underlying obligation, and unbounded delay creates operational risk. We introduce an aging function ℬ(Δτ) that adds an incrementally growing bonus to any task that has been waiting beyond a threshold age:
Under the MDPQ policy with aging function ℬ satisfying Bmax > 0 and μage > 0, and assuming system stability (ρ < 1), every task in the queue is processed in finite expected time. Specifically, any task 𝒯 with score P(𝒯, τa) = P0 will reach priority class 1 within at most
Δτpromote ≤ Δτ0 − (1/μage) · ln(1 − (P1 − P0) / Bmax)
hours of waiting, where P1 is the minimum score threshold for class 1.
8. Priority Class Assignment
The continuous score P(𝒯, τ) is discretised into K priority classes via thresholds θ1 > θ2 > … > θK−1:
9. System Stability and Throughput
For the MDPQ system to be stable, the standard non-preemptive priority queue condition must hold:
Little's Law provides the fundamental throughput identity. For each priority class k in steady state:
10. Implementation Architecture
The MDPQ model maps naturally to a priority heap implementation. At each scheduling cycle (configurable: 100ms to 5 minutes depending on throughput), all tasks in the waiting state are re-scored and the heap is rebuilt. This ensures that aging bonuses and deadline amplification are applied continuously rather than only at arrival.
import math import heapq from dataclasses import dataclass, field from typing import Optional import time as _time class TaskType: RENEGOTIATION = "renegotiation" MANDATE_EXPIRY = "mandate_expiry" SEPA_RETURN = "sepa_return" FRAUD_DISPUTE = "fraud_dispute" NOSTRO_BREAK = "nostro_break" @dataclass class Task: id: str arrival: float # Unix timestamp deadline: float # Unix timestamp; float('inf') if none amount: float # Exposure in base currency reg_level: int # Regulatory obligation 1–5 cpty_tier: int # Counterparty tier 1–5 n_dependents: int = 0 escalated: bool = False task_type: str = TaskType.RENEGOTIATION class MDPQScorer: """Multi-Dimensional Priority Queue scorer (Eq. 8).""" def __init__( self, weights: tuple[float,...] = (0.30, 0.28, 0.22, 0.12, 0.08), A0: float = 1_000, A_max: float = 5_000_000, alpha: float = 1.5, # Urgency curvature beta_h: float = 4.0, # Urgency horizon (hours) gamma: float = 1.5, # Regulatory convexity omega: float = 0.5, # Max deadline amplification delta: float = 0.25, # Amplification decay (1/h) B_max: float = 0.30, # Max aging bonus mu_age: float = 0.05, # Aging rate (1/h) age_onset: float = 2.0, # Aging onset (hours) eta: float = 5.0, # Dependency half-saturation kappa: float = 0.25, # Escalation bonus ): self.w = weights self.A0, self.A_max = A0, A_max self.alpha, self.beta = alpha, beta_h * 3600 # convert to seconds self.gamma = gamma self.omega, self.delta = omega, delta / 3600 self.B_max, self.mu_age = B_max, mu_age / 3600 self.age_onset = age_onset * 3600 self.eta, self.kappa = eta, kappa self._R_max = 5 def _f1_financial(self, amount: float) -> float: return min(1.0, math.log1p(amount / self.A0) / math.log1p(self.A_max / self.A0)) def _f2_urgency(self, now: float, deadline: float) -> float: delta_t = max(deadline - now, 1e-3) # Eq. (2) return 1.0 - math.exp(-self.alpha * self.beta / delta_t) def _f3_regulatory(self, r: int) -> float: # Eq. (3) g = self.gamma return (math.exp(g * (r - 1)) - 1) / (math.exp(g * (self._R_max - 1)) - 1) def _f4_counterparty(self, c: int) -> float: # Eq. (4) return math.log(self._R_max - c + 2) / math.log(self._R_max + 1) def _f5_dependency(self, n: int) -> float: # Eq. (5) return 1.0 - 1.0 / (1.0 + n / self.eta) def score(self, task: Task, now: float) -> float: # Dimension scores f = [ self._f1_financial(task.amount), self._f2_urgency(now, task.deadline), self._f3_regulatory(task.reg_level), self._f4_counterparty(task.cpty_tier), self._f5_dependency(task.n_dependents), ] raw = sum(self.w[i] * f[i] for i in range(5)) # Deadline amplification Ω (Eq. 7) delta_t_h = (task.deadline - now) / 3600 omega_t = 1.0 + self.omega * math.exp(-self.delta * (task.deadline - now)) # Aging bonus ℬ (Eq. 13) age = now - task.arrival aging = self.B_max * (1.0 - math.exp( -self.mu_age * max(age - self.age_onset, 0) )) # Escalation bonus κε esc = self.kappa if task.escalated else 0.0 return omega_t * raw + aging + esc # Eq. (8) def assign_class(self, score: float, thresholds: tuple = (0.75, 0.50, 0.30)) -> int: """Returns 1 (highest) to 4 (lowest). Eq. (14).""" for k, θ in enumerate(thresholds, start=1): if score >= θ: return k return len(thresholds) + 1
The scheduler wraps the scorer in a heap re-evaluation loop. Because P(𝒯, τ) is a function of the current time τ through f2, Ω, and ℬ, the effective priority of every task changes continuously. A naïve approach rebuilds the entire heap every cycle; a more efficient implementation tracks the maximum possible priority change rate per task (bounded by the derivatives of f2 and Ω) and only triggers re-evaluation when the change exceeds a configured epsilon—reducing scheduler overhead by an order of magnitude in typical operating conditions.